Shutdown socketserver serve_forever() in one-thread Python application

samuelg0rd0n picture samuelg0rd0n · Apr 10, 2012 · Viewed 36.1k times · Source

I know that socketserver has a method shutdown() which causes server to shut down but this only works in multiple threads application since the shutdown needs to be called from different thread than the thread where serve_forever() is running.

My application handles only one request at time so I do not use separate threads for handling requests and I am unable to call shutdown() because it causes deadlock (it's not in the docs but it's stated directly in the source code of socketserver).

I will paste here a simplified version of my code for better understanding:

import socketserver

class TCPServerV4(socketserver.TCPServer):
  address_family = socket.AF_INET
  allow_reuse_address = True

class TCPHandler(socketserver.BaseRequestHandler):
  def handle(self):
    try:
       data = self.request.recv(4096)
    except KeyboardInterrupt:
       server.shutdown()

server = TCPServerV4((host, port), TCPHandler)
server.server_forever()

I am aware that this code is not working. I just wanted to show you the thing I would like to accomplish - to shutdown server and quit the application while waiting for incoming data when the user presses CtrlC.

Answer

dmitry_romanov picture dmitry_romanov · Mar 20, 2014

You can start another thread locally, in your handler, and call shutdown from there.

Working demo:

#!/usr/bin/env python
# -*- coding: UTF-8 -*-

import SimpleHTTPServer
import SocketServer
import time
import thread

class MyHandler(SimpleHTTPServer.SimpleHTTPRequestHandler):
    def do_POST(self):
        if self.path.startswith('/kill_server'):
            print "Server is going down, run it again manually!"
            def kill_me_please(server):
                server.shutdown()
            thread.start_new_thread(kill_me_please, (httpd,))
            self.send_error(500)

class MyTCPServer(SocketServer.TCPServer):
    def server_bind(self):
        import socket
        self.socket.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.socket.bind(self.server_address)

server_address = ('', 8000)
httpd = MyTCPServer(server_address, MyHandler)
try:
    httpd.serve_forever()
except KeyboardInterrupt:
    pass
httpd.server_close()

Few notes:

  1. To kill server, do POST request to http://localhost:8000/kill_server.
  2. I create function which calls server.shutdown() and run it from another thread to solve the problem we discuss.
  3. I use advice from Binding Socket: “Address already in use” to make socket instantly avaliable for reuse (you can run server again without having [Errno 98] Address already in use error). With stock TCPServer you will have to wait for connection to timeout to run you server again.