Home > Enterprise >  ThreadedTCPServer works unless client is a separate process
ThreadedTCPServer works unless client is a separate process

Time:09-17

I adapted the code below from the Python documentation here.

# filename: example.py
from datetime import datetime
import socket
import socketserver
import sys
import threading
import time

class ThreadedTCPRequestHandler(socketserver.BaseRequestHandler):
    def handle(self):
        data = str(self.request.recv(1024), 'ascii')
        cur_thread = threading.current_thread()
        response = bytes("{}: {}".format(cur_thread.name, data), 'ascii')
        self.request.sendall(response)

class ThreadedTCPServer(socketserver.ThreadingMixIn, socketserver.TCPServer):
    allow_reuse_address = True

def client(ip, port, message):
    with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock:
        sock.connect((ip, port))
        sock.sendall(bytes(message, 'ascii'))
        response = str(sock.recv(1024), 'ascii')
        print("Received: {}".format(response))

if __name__ == "__main__":
    HOST, PORT = "127.0.0.1", 9999
    if sys.argv[1] == "server":
        server = ThreadedTCPServer((HOST, PORT), ThreadedTCPRequestHandler)
        with server:
            ip, port = server.server_address
            print(f'{ip} {port}')
            server_thread = threading.Thread(target=server.serve_forever)
            server_thread.start()

            # these work
            client(ip, port, "Hello World 1 at "   str(datetime.now()))
            time.sleep(1)
            client(ip, port, "Hello World 2 at "   str(datetime.now()))
            time.sleep(2)
            client(ip, port, "Hello World 3 at "   str(datetime.now()))

    if sys.argv[1] == "client":
        # these do not work, why?
        client(HOST, PORT, "Hello World 1 at "   str(datetime.now()))
        time.sleep(1)
        client(HOST, PORT, "Hello World 2 at "   str(datetime.now()))
        time.sleep(2)
        client(HOST, PORT, "Hello World 3 at "   str(datetime.now()))

Running this code as a server works as expected:

$ python3 example.py server
127.0.0.1 9999
Received: Thread-2: Hello World 1 at 2021-09-14 20:02:25.135218
Received: Thread-3: Hello World 2 at 2021-09-14 20:02:26.140889
Received: Thread-4: Hello World 3 at 2021-09-14 20:02:28.143664

I leave this terminal tab running (the program is waiting for additional clients to connect), open a new tab and run the following, but it produces an error:

$ python3 example.py client
Traceback (most recent call last):
  File "/Users/ashroyer-admin/repo/phd-courses/2021-F-cloudcomp/thred.py", line 45, in <module>
    client(HOST, PORT, "Hello World 1 at "   str(datetime.now()))
  File "/Users/ashroyer-admin/repo/phd-courses/2021-F-cloudcomp/thred.py", line 20, in client
    sock.connect((ip, port))
ConnectionRefusedError: [Errno 61] Connection refused

CodePudding user response:

ConnectionRefused typically means there is no server listening at the specified IP/Port (or its backlog is full, which is unlikely in this example). Chances are, the server object is simply not running anymore by the time you try to connect the other terminal clients. Just because you left the server's terminal open doesn't mean the server itself is still running in that terminal.

The server is likely terminating after its main thread makes its 3 calls to client(). You are running serve_forever() in another thread, not in the main thread that creates the server and calls client(). There is nothing afterwards - no loop, no event, etc - that the code is waiting on to keep the script running so the server_thread can continue servicing new clients.

When the with server: block ends, the server object gets closed. And when the main thread is done, the script exits.

What I would suggest is either:

  • get rid of server_thread and the 3 calls to client() altogether. Just call server.serve_forever() after print(f'{ip} {port}'), and then use other terminals to test your client() code.

  • or, at least remove the with server: block. Apparently, "the server thread isn't daemonic so it keeps running when main exits. Remove with server: and it works fine." (thanks
    @MarkTolonen
    )

  • Related