Shutdown socketserver serve_forever() in one-thread Python application Shutdown socketserver serve_forever() in one-thread Python application python python

Shutdown socketserver serve_forever() in one-thread Python application


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

Working demo:

#!/usr/bin/env python# -*- coding: UTF-8 -*-import SimpleHTTPServerimport SocketServerimport timeimport threadclass 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:    passhttpd.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.


The SocketServer library uses some weird ways of handling inherited attributes ( guessing due to use of old style classes). If you create a server and list it's protected attributes you see:

In [4]: server = SocketServer.TCPServer(('127.0.0.1',8000),Handler)In [5]: server._server._BaseServer__is_shut_downserver.__init__server._BaseServer__shutdown_requestserver.__module__server.__doc__server._handle_request_nonblock

If you just add the following in your request handler:

self.server._BaseServer__shutdown_request = True

The server will shutdown. This does the same thing as calling server.shutdown(), just without waiting (and deadlocking the main thread) until it's shutdown.


You should call shutdown in other thread actually as pointed in source code:

 def shutdown(self):        """Stops the serve_forever loop.        Blocks until the loop has finished. This must be called while        serve_forever() is running in another thread, or it will        deadlock.        """        self.__shutdown_request = True        self.__is_shut_down.wait()