import socket import threading """ Clients can send messages to other clients by sending a message to the server in the format ": ". The server will then forward the message to the recipient client in the format ": ". Messages are \n-terminated. The server will also send status messages for the following events: - A client connects: - [Server to others] -> "0: Client 1 has connected" - [Server to the client] -> "0: Your id is 1" - A client disconnects: - [Server to others] -> "0: Client 1 has disconnected" - A client sends a message to a non-existent recipient: - [Server to the client] -> "0: Recipient 2 not found" Example: Client 1 sends a message to client 2: [Client 1] -> "2: Hello, client 2!" The server will forward the message to client 2: [Server] -> "1: Hello, client 2!" """ class ChatServer: def __init__(self, host: str = '0.0.0.0', port: int = 12345): self.server_socket: socket.socket = socket.socket(socket.AF_INET, socket.SOCK_STREAM) self.server_socket.bind((host, port)) self.server_socket.listen(5) self.clients: dict[int, socket.socket] = {} self.client_id: int = 1 print(f"Server started on {host}:{port}") def send_message(self, sender_id: int, recipient_id: int, message: str, rec: bool = False) -> None: print(f"{sender_id} -> {recipient_id}: {message}") if recipient_id in self.clients: self.clients[recipient_id].sendall(f"{sender_id}: {message}".encode()) else: if rec: return self.send_message(0, sender_id, f"Recipient {recipient_id} not found", True) def broadcast(self, skip_id: int, message: str) -> None: print(f"0 -> all: {message}") for client_id in self.clients: if client_id != skip_id: self.clients[client_id].sendall(f"0: {message}".encode()) def handle_client(self, client_id: int) -> None: try: buffer = "" while True: data = self.clients[client_id].recv(1024).decode() if not data: break buffer += data messages = buffer.split("\n") buffer = messages.pop() for message in messages: recipient_id, message = message.split(": ", 1) self.send_message(client_id, int(recipient_id), message) except (socket.timeout, ConnectionResetError, BrokenPipeError): pass finally: self.broadcast(client_id, f"Client {client_id} has disconnected") del self.clients[client_id] def start(self) -> None: print("Server is running and waiting for clients to connect...") while True: client_socket, _ = self.server_socket.accept() client_socket.settimeout(5) self.clients[self.client_id] = client_socket self.broadcast(self.client_id, f"Client {self.client_id} has connected") self.send_message(0, self.client_id, f"Your id is {self.client_id}") receiver = threading.Thread(target=self.handle_client, args=(self.client_id,)) receiver.start() self.client_id += 1 if __name__ == "__main__": server = ChatServer() server.start()