import threading import socket class MyThread(threading.Thread): def __init__(self, conn): threading.Thread.__init__(self) # call superclass constructor self.conn = conn # add a field to this object def run(self): # will be run by .start() in the new thread while True: buffer = self.conn.recv(4096) if not buffer: print('client',self.conn.fileno(),'disconnected') break print(f"[{self.conn.fileno()}] recv() binary:", ' '.join('{:02X}'.format(byte) for byte in buffer)) try: print(f"[{self.conn.fileno()}] recv():", buffer.decode('utf-8')) except UnicodeDecodeError as ex: print(f"[{self.conn.fileno()}] recv()ed non-string data") self.conn.send(f'recv()ed {len(buffer)} bytes\n'.encode('utf-8')) self.conn.close() if __name__ == '__main__': # checks that this file was run, not imported import sys if len(sys.argv) != 2: print('USAGE:', sys.argv[0], '') quit(1) port = int(sys.argv[1]) print(f'Binding to port {port}. Visit http://localhost:{port}/ to interact with your server!') with socket.socket(socket.AF_INET, socket.SOCK_STREAM) as sock: sock.bind(('', port)) sock.listen(10) while True: try: conn, addr = sock.accept() print("Client connected from", addr, f"(fd={conn.fileno()})") client_thread = MyThread(conn) client_thread.daemon = True client_thread.start() except: # this ensures the socket is closed on a Ctrl+C # raise # this will show the error instead of closing the socket break