I have a simple TCP server. There is an issue that occurs when I connect to the server using the Google Chrome browser: after a request, it seems to open another socket to send the next request there quickly next time. When such a socket is opened, I cannot restart my server gracefully. I can use the allow_reuse_address
hack but it is not the thing I want to do because the port is still busy after the server shutdown, and if I wish another server to listen to this port, it is unable to do this.
I am sure there may be other cases when I don't want my port to be held by a nasty client and I would like to have the ability to force disconnect it. If I force to disconnect the server and then try to connect again, I get
OSError: [Errno 98] Address already in use
Here is an example. Let us imagine some "dirty client" connecting to my server and doing nothing (simply to consume the resources of my server). I want to shut down my server (let us say, for maintenance). I cannot do this until the client disconnects. I wish to disconnect it manually from the server-side.
from http.server import BaseHTTPRequestHandler
from socket import socket, AF_INET, SOCK_STREAM
from socketserver import TCPServer
from threading import Thread
from time import sleep, time
def dirty_client():
client_socket = socket(AF_INET, SOCK_STREAM)
client_socket.connect(("127.0.0.1", 8080))
sleep(10)
client_socket.close()
if __name__ == "__main__":
# Spawn TCP server
server = TCPServer(("127.0.0.1", 8080), BaseHTTPRequestHandler)
thread = Thread(target=server.serve_forever)
thread.start()
# Block it with a connection (should be on client side)
hang_thread = Thread(target=dirty_client)
hang_thread.start()
sleep(1)
# Let it hang
start = time()
print("Shutting down...")
server.shutdown()
server.server_close()
print(f"Success in {time() - start} seconds")
thread.join()
hang_thread.join()
The server hangs on the moment the request handler reads the socket. There is no timeout for reading and I cannot close the file during the read. The server itself can be shut down during the read, but the port will not be freed in this case.
It is possible to use BaseRequestHandler
in this example — after it, the port is also in use. Though, BaseHTTPRequestHandler
waits until the port is freed.