Jan Bodnar
Jan Bodnar

Reputation: 11647

Python closing socket and connection in a threaded server?

We have a Python socket threaded server example. It is a slightly modified version from https://stackoverflow.com/a/23828265/2008247. The example works and my tests confirm that it performs better than the blocking server.

But in the example, the socket and the connection objects are not closed. Both objects have close() method. (The close method on a connection is called only on Exception. I would expect it to be called for each connection when it ends.) Do we not need to somehow call them? If so, how?

#!/usr/bin/env python

import socket
import threading

class ThreadedServer():

    def __init__(self, host, port):

        self.host = host
        self.port = port
        self.sock = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
        self.sock.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
        self.sock.bind((self.host, self.port))

    def listen(self):

        self.sock.listen(5)

        while True:

            con, address = self.sock.accept()
            con.settimeout(60)
            threading.Thread(target=self.listenToClient,
                             args=(con, address)).start()

    def listenToClient(self, con, address):

        while True:
            try:
                data = con.recv(1024)
                if data:
                    # Set the response to echo back the recieved data
                    response = data
                    con.send(response)
                else:
                    raise Exception('Client disconnected')
            except:
                con.close()
                return False


def main():

    ThreadedServer('', 8001).listen()


if __name__ == "__main__":
    main()

Upvotes: 3

Views: 5534

Answers (1)

CristiFati
CristiFati

Reputation: 41137

According to [Python.Docs]: socket.close():

Sockets are automatically closed when they are garbage-collected, but it is recommended to close() them explicitly, or to use a with statement around them.

So, all the sockets will be closed eventually. However, if server runs for a long time and it (quickly) creates / destroys a lots of connections, you might end up in the situation of running out of file descriptors, and as a consequence new sockets couldn't be created, therefore it's good to have them manually closed.

There are 2 cases.

1. Close the server socket

Things are simple. Add a "destructor", or better: finalizer ([Python.Docs]: Data model - object.__del__(self)) to your ThreadedServer class:

def __del__(self):
    #print("Closing server socket:", self.sock)
    self.sock.shutdown(socket.SHUT_RDWR)
    self.sock.close()

This is not really necessary, since the server will be destroyed at program end, and all the resources will be freed at that point anyway.

2. Close each client socket

Either:

  • Add a finally clause ([Python.Docs]: Compound statements - The try statement) to the try in listenToClient (while we are at this point, you could also rename the method to something like listen_to_client):

    # ...
    try:
        # Socket operation that might raise exceptions (con.recv)
    except:
        return False
    finally:
        #print("Closing connection:", con)
        con.shutdown(socket.SHUT_RDWR)
        con.close()
    
  • Extend the Thread class, and use that in ThreadedServer.listen (this is more elegant):

    class ConnectionHandlingThread(threading.Thread):
        def __init__(self, target, connection, address):
            self.connection = connection
            super().__init__(target=target, args=(connection, address))
    
        def run(self):
            super().run()
            #print("Closing connection:", self.connection)
            self.connection.shutdown(socket.SHUT_RDWR)
            self.connection.close()
    

    and its usage:

    ConnectionHandlingThread(listen_to_client, con, address).start()
    

Alternative

All this looks a bit like reinventing the wheel. The boilerplate for this exact scenario is already present in the socketserver module (part of Python standard library). For more details, check [Python.Docs]: socketserver - Asynchronous Mixins.

Upvotes: 3

Related Questions