Search code examples
pythonsocketsnetwork-programmingconnection

Handling multiple connections in python with sockets


I have a code which works perfectly for one connection. I have seen two options for multi-client handling but I don't really understand it. Here is the server socket code:

with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as listening_sock:
    listening_sock.bind(('', port))
    listening_sock.listen()
    client_soc, client_address = listening_sock.accept()
    client_soc.sendall('200#Welcome to my server!'.encode())
    print(f'Address {client_soc.getsockname()[0]} connected with port {client_soc.getsockname()[1]}')
    while True:
        # get message
        msg = client_soc.recv(1024).decode()
        # receive log print:
        print(f'"{msg}" sent from {client_soc.getsockname()[0]}')
        if 'Quit' in msg:
            client_soc.sendall('200#Thanks for using my server!'.encode())
            client_soc.close()
        elif '0' < msg.split('#')[0] <= '9':  # one of the valid actions
            answer = call_action(msg.split('#')[0], db, msg.split('#')[1])  # the answer for given parameter
            client_soc.sendall("200#".encode() + answer.encode())

If I have only one connection it works good and last thing I need to add is option for multiple-client handling. What is the shortest and easiest way to do it?


Solution

  • The code only calls accept once. Instead, call accept in a while loop and create a thread for each client connection so they are handled in parallel. Use the following pattern as an example:

    import socket
    import threading
    
    # Thread to handle each "client_soc" connection
    def handler(client_soc):
        ...
        client_soc.close()
    
    with socket.socket() as listening_sock:
        listening_sock.bind(('', 8000))
        listening_sock.listen()
        while True:
            client_soc, client_address = listening_sock.accept()
            # Send each "client_soc" connection as a parameter to a thread.
            threading.Thread(target=handler,args=(client_soc,), daemon=True).start() 
    

    There is also a built-in socket server that simplifies this process. Here's a tested example echo server that echoes back newline-terminated data:

    from socketserver import ThreadingTCPServer,StreamRequestHandler
    
    class echohandler(StreamRequestHandler):
        def handle(self):
            print(f'Connected: {self.client_address[0]}:{self.client_address[1]}')
            while True:
                # get message
                msg = self.rfile.readline()
                if not msg:
                    print(f'Disconnected: {self.client_address[0]}:{self.client_address[1]}')
                    break # exits handler, framework closes socket
                print(f'Received: {msg}')
                self.wfile.write(msg)
                self.wfile.flush()
    
    server = ThreadingTCPServer(('',8000),echohandler)
    server.serve_forever()