Python accepted socket connection not closing

Viewed 65

I've written a Pi Hardware Interface Server (phis) that uses http protocol to control the hardware connected to my Raspberry Pi (relays, analog measurements, etc). It processes simple requests and responds with plain text. It has been working flawlessly for years and I have written extensive browser-based interfaces to the system. Here's the basic structure:

listen_socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
listen_socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
listen_socket.bind((HOST, PORT))
listen_socket.listen(1)

read_list = [listen_socket]

while True:
    readable, writeable, errored = select.select(read_list,[], [], 2)
    if len(readable) == 0: # select timed out
        continue

    client_connection, client_address = listen_socket.accept()
    try:
        request = client_connection.recv(1024)
    except Exception:
        print "Exception trying to recv, %s" % time.strftime(date_format)
        sys.exc_clear()
        client_connection.close()
        continue

    words = request.split()

    http_response = """\
HTTP/1.1 200 OK
"""
    try:
        client_connection.sendall(http_response)
    except Exception:
        print "Exception trying to send 1, %s" % time.strftime(date_format)
        sys.exc_clear()
        client_connection.close()
        continue

    http_response = """\
Content-Type: text/plain; charset=utf-8

"""

# perform some action and add text to the http_response (like current CPU temperature, etc)

    try:
        client_connection.sendall(http_response)
    except Exception:
        # log the error, then...
        sys.exc_clear()
        client_connection.close()
        continue

    client_connection.close()

    if words[1] == '/exit':
        sys.exit(0)

The problem occurs when the command is to start mjpg_streamer. The stream starts just fine, the response is sent and I can make more connections and issue new commands, but the client_connection.close() never actually closes the connection and the browser just sits there waiting for more stuff that never comes. The phis is still accepting new connections and servicing those, but if I exit the server with the request "http://pi:4000/exit", it does so cleanly, but it wont start again (socket in use) unless I first issue the request to stop mjpg_streams (which also works just fine).

mjpg_streamer already starts in (what seems to be) a daemonized background mode, but in my attempts to get this working, I have issued the command to start it in every python "start your background child and leave it running" method I could find (os.system(), subprocess.Popen(), os.fork() and os.fork() twice with os.setsid() in between). Nothing worked.

If it matters, I'm actually calling a perl script from the phis that starts mjpg_steamer with command line arguments based on arguments passed to the perl script.

I have googled much on this and found others with connections that would not close, but not with connections that close perfectly unless some background socket-based app is spawned while the connection is active. Anyone have any idea what's wrong?

1 Answers

Found the answer in this post ("Duh" moment the instant I saw it!)

I had forgotten to close the connected and listening sockets in the forked child, which were inherited by the spawned daemon and stayed open as long as it runs. Here's the code I'm using to spawn a process that will be left running (daemonized):

def spawnDaemon(syscmd):
    # do the UNIX double-fork magic, see Stevens' "Advanced 
    # Programming in the UNIX Environment" for details (ISBN 0201563177)
    try: 
        pid = os.fork() 
        if pid > 0:
            # parent process, wait for first child, then return and keep running
            os.wait()
            #print "first parent returning"
            return
    except OSError, e:
        print >>sys.stderr, "fork #1 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    # close the copy of the socket the request came in on
    client_connection.close()

    # close the copy of the server's main listening socket
    listen_socket.close()

    # change process group to detach the child (mjpeg_streamer) from the parent phis
    os.setsid()

    # do second fork
    #print "trying second fork"
    try: 
        pid = os.fork() 
        if pid > 0:
            # exit from second parent
            #print "second parent exiting"
            sys.exit(0) 
    except OSError, e: 
        print >>sys.stderr, "fork #2 failed: %d (%s)" % (e.errno, e.strerror) 
        sys.exit(1)

    # execute the command
    os.system(syscmd)

    # all done, exit 2nd child
    #print "exiting after executing syscmd"
    sys.exit(0)
# spawnDaemon()
Related