Alois Mahdal
Alois Mahdal

Reputation: 11233

Connections outlive SocketServer.TCPServer?

How can I make a TCP server properly close sockets?

I wrote a simple TCP server to echo some information back to client. Now I would like to use it on localhost:8088 and stop it using killall and restart at any time as I'm working on the code.

However, I'm having trouble making it close all sockets and "free" the address, so when I quickly make a few tests, fix something in the code and then stop (Ctrl+C) and start the server again, I get socket.error: [Errno 98] Address already in use.

When I sudo netstat -ntap, I can still see few 127.0.0.1:8088 sockets in TIME_WAIT state. So I have to wait until they "die out".

My test case:

#!/usr/bin/python
import SocketServer

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\nhello."
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

What am I doing wrong? Shouldn't self.request.close() be enough?

I'm trying this on Debian with Python 2.7.3, although I need to support Squeeze with Python 2.6 as well.

Upvotes: 1

Views: 1767

Answers (2)

tdelaney
tdelaney

Reputation: 77337

The TCP stack puts the port into timed-wait for awhile (something like 30 seconds to multiple minutes depending on your system). You can change that behavior with the SO_REUSEADDR socket option. The trick is that the option must be set before the port is bound.

If you have the raw socket, you can:

s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1) 

SocketServer lets you set the option globally with the allow_reuse_address attribute:

SocketServer.TCPServer.allow_reuse_address = True

Or you can do it when you create the server:

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
    server.allow_reuse_address = True
    server.server_bind()
    server.serve_forever()

Or even override the bind method:

class MyTCPServer(SocketServer.TCPServer):

    def server_bind(self):
        self.allow_reuse_address = True
        super(MyTCPServer, self).server_bind()

Here are 3 solutions that worked for me on my Windows machine.

(1) set globally

#!/usr/bin/python
import SocketServer
import time

SocketServer.allow_reuse_address = True

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

(2) Set locally

#!/usr/bin/python
import SocketServer
import time

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

def main():
    server = SocketServer.TCPServer((HOST, PORT), MyEchoHandler, False)
    server.allow_reuse_address = True
    server.server_bind()
    server.server_activate()
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

(3) Inherit

#!/usr/bin/python
import SocketServer
import time

class MyEchoHandler(SocketServer.BaseRequestHandler):

    def handle(self):
        self.data = self.request.recv(1024).strip()
        # do something smart with the data, but for now, just say hello.
        self.reply = "Content-Type: text/plain\r\n\r\n" + time.asctime()
        self.request.send(self.reply)
        self.request.close()

class MyTCPServer(SocketServer.TCPServer):

    def server_bind(self):
        self.allow_reuse_address = True
        SocketServer.TCPServer.server_bind(self)

def main():
    server = MyTCPServer((HOST, PORT), MyEchoHandler)
    server.serve_forever()

if __name__ == '__main__':
    HOST, PORT = "localhost", 8088
    main()

Upvotes: 5

Dale Wilson
Dale Wilson

Reputation: 9434

You can call server.shutdown() but you have to do it from another thread 8-( IMO this is weak area in Python's TCPServer.

Upvotes: -1

Related Questions