Reputation: 11647
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
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.
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.
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()
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