Rooms and broadcasting
A naive websocket server broadcasts every message to every connected client. That works for ten users and breaks at ten thousand. Rooms are the pattern that scales: clients subscribe to named channels, and the server only sends messages for those channels to the clients who asked.
import { WebSocket } from 'ws';
// channel name -> set of sockets
const rooms = new Map<string, Set<WebSocket>>();
export function joinRoom(ws: WebSocket, channel: string) {
if (!rooms.has(channel)) rooms.set(channel, new Set());
rooms.get(channel)!.add(ws);
}
export function leaveRoom(ws: WebSocket, channel: string) {
rooms.get(channel)?.delete(ws);
}
export function leaveAllRooms(ws: WebSocket) {
for (const members of rooms.values()) {
members.delete(ws);
}
}
export function broadcastToRoom(channel: string, data: unknown) {
const members = rooms.get(channel);
if (!members) return;
const payload = JSON.stringify(data);
for (const client of members) {
if (client.readyState === WebSocket.OPEN) {
client.send(payload);
}
}
}A tiny in-memory room registry. Join, leave, broadcast.
Read the code top to bottom. Rooms are just a Map from a channel name to a set of sockets. Joining adds the socket. Leaving removes it. Broadcasting iterates members and sends to each one that is still open. Simple enough to fit on one screen, powerful enough to scale to thousands of live clients per process.
Critical habit: on every socket disconnect, call leaveAllRooms. If you do not, the room keeps a reference to the dead socket, the Set grows forever, and you leak memory. Wire leaveAllRooms to the ws.on('close') event and the leak goes away.
Quiz: Quiz
Loading practiceโฆ