Normadize
Normadize

Reputation: 1222

Python: How to set a timeout on receiving data in SocketServer.TCPServer

I've read quite a few things and this still escapes me. I know how to do it when using raw sockets. The following works just fine, times out after 1 second if no data is received:

sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
sock.bind((HOST, PORT))
sock.listen(1)
while 1:
    conn, addr = sock.accept()
    data = ''
    conn.settimeout(1)
    try:
        while 1:
            chunk = conn.recv(1024)
            data += chunk
            if not chunk:
                break
        print 'Read: %s' % data
        conn.send(data.upper())
    except (socket.timeout, socket.error, Exception) as e:
        print(str(e))
    finally:
        conn.close()
        print 'Done'

But when trying something similar when using SocketServer.TCPServer with SocketServer.BaseRequestHandler (not with SocketServer.StreamRequestHandler where I know how to set a timeout) it seems not as trivial. I didn't find a way to set a timeout for receiving the data. Consider this snippet (not complete code):

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        data = ''
        while 1:
            chunk = self.request.recv(1024)
            data += chunk
            if not chunk:
                break

if __name__ == "__main__":
    HOST, PORT = "0.0.0.0", 9987
    SocketServer.TCPServer.allow_reuse_address = True
    server = SocketServer.TCPServer((HOST, PORT), MyTCPHandler)
    server.serve_forever()

Suppose the client sends only 10 bytes. The while loop runs once, chunk is not empty, so then executes self.request.recv() again but the client has no more data to send and recv() blocks indefinitely ...

I know I can implement a small protocol, check for terminating strings/chars, check message length etc., but I really want to implement a timeout as well for unforeseen circumstances (client "disappears" for example).

I'd like to set and also update a timeout, i.e. reset the timeout after every chunk, needed for slow clients (though that's a secondary issue at the moment).

Thanks in advance

Upvotes: 2

Views: 7108

Answers (2)

domids
domids

Reputation: 525

You can do the same thing with SocketServer.BaseRequestHandler.request.settimeout() as you did with the raw socket.

eg:

class MyTCPHandler(SocketServer.BaseRequestHandler):
    def handle(self):
        self.request.settimeout(1)
        ...

In this case self.request.recv() will terminate if it takes longer than 1 second to complete.

Upvotes: 4

Schilcote
Schilcote

Reputation: 2394

class MyTCPHandler(SocketServer.BaseRequestHandler):
timeout=5
...

... will raise an exception (which serve_forever() will catch) and shut down the connection if 5 seconds pass without receiving data after calling recv(). Be careful, though; it'll also shut down your connection if you're sending data for more than 5 seconds as well.

This may be Python 3 specific, mind, but it works for me.

Upvotes: 0

Related Questions