Skip to content Skip to sidebar Skip to footer

Shutdown For Socketserver Based Python 3 Server Hangs

I am working on a 'simple' server using a threaded SocketServer in Python 3. I am going through a lot of trouble implementing shutdown for this. The code below I found on the inter

Solution 1:

shutdown() works as expected, the server has stopped accepting new connections, but python still waiting for alive threads to terminate.

By default, socketserver.ThreadingMixIn will create new threads to handle incoming connection and by default, those are non-daemon threads, so python will wait for all alive non-daemon threads to terminate.

Of course, you could make the server spawn daemon threads, then python will not waiting:

The ThreadingMixIn class defines an attribute daemon_threads, which indicates whether or not the server should wait for thread termination. You should set the flag explicitly if you would like threads to behave autonomously; the default is False, meaning that Python will not exit until all threads created by ThreadingMixIn have exited.

classThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    daemon_threads = True

But that is not the ideal solution, you should check why threads never terminate, usually, the server should stop processing connection when no new data available or client shutdown connection:

import socketserver
import threading


shutdown_evt = threading.Event()


classservice(socketserver.BaseRequestHandler):
    defhandle(self):
        self.request.setblocking(False)
        whileTrue:
            try:
                msg = self.request.recv(1024)
                if msg == b'shutdown':
                    shutdown_evt.set()
                    breakelif msg:
                    self.request.send(b'you said: ' + msg)
                if shutdown_evt.wait(0.1):
                    breakexcept Exception as e:
                breakclassThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    passdefrun():
    SERVER = ThreadedTCPServer(('127.0.0.1', 10000), service)
    server_thread = threading.Thread(target=SERVER.serve_forever)
    server_thread.daemon = True
    server_thread.start()
    input("Press enter to shutdown")
    shutdown_evt.set()
    SERVER.shutdown()


if __name__ == '__main__':
    run()

Solution 2:

I tried two solutions to implement a tcp server which runs on Python 3 on both Linux and Windows (I tried Windows 7):

  • using socketserver (my question) - shutdown is not working
  • using asyncio (posted an answer for that) - does not work on Windows

Both solutions have been based upon search results on the web. In the end I had to give up on the idea of finding a proven solution because I could not find one. Consequently I implemented my own solution (based on gevent). I post it here because I hope it will be helpful for others to avoid stuggeling the way I did.

# -*- coding: utf-8 -*-from gevent.server import StreamServer
from gevent.pool import Pool


classEchoServer(StreamServer):

    def__init__(self, listener, handle=None, spawn='default'):
        StreamServer.__init__(self, listener, handle=handle, spawn=spawn)

    defhandle(self, socket, address):
        print('New connection from %s:%s' % address[:2])
        socket.sendall(b'Welcome to the echo server! Type quit to exit.\r\n')

        # using a makefile because we want to use readline()
        rfileobj = socket.makefile(mode='rb')
        whileTrue:
            line = rfileobj.readline()
            ifnot line:
                print("client disconnected")
                breakif line.strip().lower() == b'quit':
                print("client quit")
                breakif line.strip().lower() == b'shutdown':
                print("client initiated server shutdown")
                self.stop()
                break
            socket.sendall(line)
            print("echoed %r" % line.decode().strip())
        rfileobj.close()


srv = EchoServer(('', 1520), spawn=Pool(20))
srv.serve_forever()

Solution 3:

after more research I found a sample that works using asyncio:

# -*- coding: utf-8 -*-import asyncio

# after further research I found this relevant europython talk:#     https://www.youtube.com/watch?v=pi49aiLBas8# * protocols and transport are useful if you do not have tons of socket based code# * event loop pushes data in# * transport used to push data back to the client# found decent sample in book by wrox "professional python"classServerProtocol(asyncio.Protocol):
    defconnection_made(self, transport):
        self.transport = transport
        self.write('Welcome')

    defconnection_lost(self, exc):
        self.transport = Nonedefdata_received(self, data):
        ifnot data or data == '':
            return
        message = data.decode('ascii')
        command = message.strip().split(' ')[0].lower()
        args = message.strip().split(' ')[1:]

        #sanity checkifnothasattr(self, 'command_%s' % command):
            self.write('Invalid command: %s' % command)
            return# run commandtry:
            returngetattr(self, 'command_%s' % command)(*args)
        except Exception as ex:
            self.write('Error: %s' % str(ex))

    defwrite(self, msg):
        self.transport.write((msg + '\n').encode('ascii', 'ignore'))

    defcommand_shutdown(self):
        self.write('Okay. shutting down')
        raise KeyboardInterrupt

    defcommand_bye(self):
        self.write('bye then!')
        self.transport.close()
        self.transport = Noneif __name__ == '__main__':
    loop = asyncio.get_event_loop()
    coro = loop.create_server(ServerProtocol, '127.0.0.1', 8023)
    asyncio.async(coro)
    try:
        loop.run_forever()
    except KeyboardInterrupt:
        pass

I understand that this is the most useful way to do this kind of network programming. If necessary the performance could be improved using the same code with uvloop (https://magic.io/blog/uvloop-blazing-fast-python-networking/).

Solution 4:

Another way to shut down the server is by creating a process/thread for the serve_forever call.

After server_forever is started, simply wait for a custom flag to trigger and use server_close on the server, and terminate the process.

streaming_server = StreamingServer(('', 8000), StreamingHandler)

FLAG_KEEP_ALIVE.value = True

process_serve_forever = Process(target=streaming_server.serve_forever)
process_serve_forever.start()

while FLAG_KEEP_ALIVE.value:
    pass

streaming_server.server_close()
process_serve_forever.terminate()

Post a Comment for "Shutdown For Socketserver Based Python 3 Server Hangs"