python TCPServer address already in use but I close the server and I use `allow_reuse_address`

后端 未结 4 985
渐次进展
渐次进展 2020-12-31 04:35

Here is my code to run the server:

class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    #....

PORT = 8089

httpd = SocketServer.TCPServer(         


        
相关标签:
4条回答
  • 2020-12-31 05:12

    It is because you have to set SO_REUSEADDRESS before you bind the socket. As you are creating and binding the socket all in one step and then setting it, it is already too late.

    0 讨论(0)
  • 2020-12-31 05:13

    The [Err 98] Address already in use is due to the fact the socket was .close() but it's still waiting for enough time to pass to be sure the remote TCP received the acknowledgment of its connection termination request (see TIME_WAIT). By default you are not allowed to bind a socket if there a socket bound to that port but you can override that with allow_reuse_address (SO_REUSEADDR)

    Although is possible to mutate TCPServer.allow_reuse_addr (as proposed in this other answer), I think is more clean to your own subclass of TCPServer where allow_reuse_address is set to True:

    import SocketServer
    import SimpleHTTPServer
    import time
    
    class MyRequestHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
        def do_GET():
            time.sleep(60)
            self.request.sendall("I'm here!")
    
    class ReuseAddrTCPServer(SocketServer.TCPServer):
        allow_reuse_address = True
    
    PORT = 8089
    
    httpd = ReuseAddrTCPServer(("", PORT), MyRequestHandler)
    httpd.daemon_threads = True
    
    
    print "Serving forever at port", PORT
    try:
        httpd.serve_forever()
    except:
        print "Closing the server."
        httpd.server_close()
        raise
    

    You can use definitely set allow_reuse_address on the instance itself (without messing with classes) but you need to use TCPServer(..., bind_and_activate=False), otherwise the socket will be bound before you have a chance to change the allow_reuse_address setting. Then you need to manually call .server_bind() and .server_activate() before serve_forever():

    ...
    httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler, bind_and_activate=False)
    httpd.allow_reuse_address = True
    httpd.daemon_threads = True
    ...
    httpd.server_bind()
    httpd.server_activate()
    httpd.serve_forever()
    
    0 讨论(0)
  • 2020-12-31 05:21

    Thanks to the other answers, I figured it out. allow_reuse_address should be on the class, not on the instance:

    SocketServer.TCPServer.allow_reuse_address = True
    httpd = SocketServer.TCPServer(("", PORT), MyRequestHandler)
    

    I'm still not sure why closing the socket didn't free it up for the next run of the server, though.

    0 讨论(0)
  • 2020-12-31 05:36

    It is because TCP TIME_WAIT.

    Somebody discovered this exact problem.

    However, if I try to stop and start the server again to test any modifications, I get a random “socket.error: [Errno 98] Address already in use” error. This happens only if a client has already connected to the server.

    Checking with netstat and ps, I found that although the process it self is no longer running, the socket is still listening on the port with status “TIME_WAIT”. Basically the OS waits for a while to make sure this connection has no remaining packets on the way.

    0 讨论(0)
提交回复
热议问题