Multi-Client Chat Server
Overview
Build a single-machine chat server that accepts many client connections and relays messages between them. Each client connects over a socket; whatever one client sends is broadcast to all the others. The focus here is the server's concurrency: accepting connections, reading from many clients at once, and fanning messages out without one slow or dead client stalling everyone else.
The classic structure is an accept loop plus one reader thread per client. A shared, thread-safe registry tracks the connected clients so a broadcast can reach them all. The hard parts are clean handling of disconnects and making sure a slow client receiver applies backpressure only to itself.
Interfaces
class ChatServer:
def __init__(self, host, port):
self._clients = {} # client_id -> ClientHandler
self._lock = Lock()
def serve_forever(self):
# accept loop: for each new socket, register a client and start its thread
...
def _handle_client(self, conn, client_id):
# read messages from this client until it disconnects, then broadcast
...
def broadcast(self, sender_id, message):
# enqueue message to every connected client except the sender
...
def _remove_client(self, client_id):
# drop a disconnected client from the registry and close its socket
...
class ClientHandler:
def __init__(self, conn):
self._conn = conn
self._outbox = Queue() # messages waiting to be sent out
# a writer thread drains _outbox to the socket
The accept loop only accepts; per-client reader threads do the I/O. Each client has its own outbound queue and writer thread, so a broadcast just enqueues to everyone — a slow socket backs up its own queue without blocking the sender or the other clients.
Inputs and outputs
- Input: TCP connections from N clients, each sending text messages.
- Output: every message is delivered to all other connected clients; disconnects are detected and cleaned up without affecting others.
Requirements
- The server handles many clients concurrently — one reader thread per client.
- The shared client registry is safe under concurrent connects and disconnects.
- A broadcast reaches all current clients except the sender.
- A slow or dead client cannot block delivery to healthy clients.
- Disconnects (and socket errors) remove the client and free its resources.
Examples
server = ChatServer("0.0.0.0", 5000)
server.serve_forever()
# alice and bob connect; alice sends "hello"
# -> bob receives "alice: hello"
# bob's connection drops
# -> server removes bob; alice keeps chatting uninterrupted
Think about it
You broadcast by writing straight to each client's socket, and one client's TCP buffer is full.
What happens to everyone else's messages while that one write blocks?
How does giving each client its own outbound queue keep the slow one from freezing the rest?
Follow-ups
One thread per client won't scale to tens of thousands. How would non-blocking I/O or an event loop (select/epoll) change the design?
How would you add rooms so a broadcast only reaches a subset of clients?
TCP gives you a byte stream, not messages. How would you frame messages so partial reads reassemble correctly?
And more...
FANG Crash Course
Fullset Newest FANG Coding/System Design Questions, Optimal Solutions and Explanations.
Step_1 Matched with a FAANG+ Senior Engineer
Step_2 Fullset FANG Coding/System Design Questions
Step_3 Solutions + step by step Explanation
