Aller au contenu principal
How systems communicate — TCP/IP, HTTP, DNS, load balancing, and security.

Networking

How systems communicate — TCP/IP, HTTP, DNS, load balancing, and security.

WebSockets, SSE & Realtime Systems

A realtime system is one where the server pushes data to the client without the client having to ask. Chat, dashboards, multiplayer state, live presence, sports scores, notifications — these all needle-by-needle to push from server to client, and none of them work over plain HTTP request/response.

The space is dominated by three transports — long-polling, Server-Sent Events (SSE), and WebSockets — and each has a shape where it wins. This topic is how to choose between them, and the connecting glue (presence, fan-out, backpressure) that makes any of them scale.

The Realtime Transport Spectrum

TransportDirectionalityCost over a minute of idleBest fit
HTTP pollingClient → serverN requests/min — wastefulNever; the historical fallback
Long-pollingServer responds when data arrives (or timeout)1 connection/sec = 60 idle requests/minWorst case: when only a single event is expected
Server-Sent Events (SSE)Server → client, unidirectional1 connection/min — true pushOne-to-many push: stock tickers, news feeds, log streams
WebSocketBidirectional, message-based1 connection/min — true pushInteractive bidirectional: chat, multiplayer, collaborative editing
WebTransport (HTTP/3-based)Bidirectional, low-latency1 connection/min — true pushThe HTTP/3-native successor to WebSocket

Three orthogonal axes matter for the choice:

  1. Who speaks when? Server-only is fine for SSE; both-need-to-talk is WebSocket.
  2. Through what middleboxes? corporate proxies and HTTP/2-only CDNs sometimes break long-lived WebSocket connections; SSE is more proxy-tolerant.
  3. What transport matters across the stack? A WebSocket is its own protocol; SSE rides on HTTP (with HTTP/2 multiplexing); if the surrounding infrastructure speaks only HTTP, SSE is the better fit.

Long-Polling

The client sends a request that the server holds open until an event is available (or a time limit). When data arrives, the server responds; the client immediately issues a new request.

  • Pro: works through every proxy, every firewall, every CDN; pure HTTP.
  • Con: every event costs one HTTP request/response overhead; under load, the overhead dominates.
  • Modern use: kept only as a fallback when SSE or WebSocket is blocked.

Server-Sent Events (SSE)

SSE is a unidirectional server → client stream on top of HTTP. The server sends Content-Type: text/event-stream; the client opens an EventSource connection (the browser reconnects automatically).

GET /events HTTP/1.1
Accept: text/event-stream

HTTP/1.1 200 OK
Content-Type: text/event-stream

data: {"msg":"hello"}

event: status
data: {"value":1}
  • Pro: native browser reconnect; HTTP/2 multiplexes many SSE streams on a single TCP connection; survives middleboxes; supports named events (event: ping).
  • Con: server-to-client only; limited concurrent connections per browser on HTTP/1.1 (the limit cleared on HTTP/2).
  • Modern use: log tails, status feeds, progressively-loaded search results, server pushes that don’t need client-side chatter.

WebSocket

WebSocket is a bidirectional, full-duplex, message-framed protocol that upgrades from an HTTP/1.1 handshake.

GET /ws HTTP/1.1
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Key: ...

HTTP/1.1 101 Switching Protocols
Upgrade: websocket
Connection: Upgrade
Sec-WebSocket-Accept: ...

After the handshake, the connection switches from HTTP semantics to WebSocket frames — small length-prefixed messages, either text or binary, in either direction.

  • Pro: bidirectional; smaller per-message overhead than HTTP; supports binary; constant connection enables quick back-and-forth.
  • Con: requires its own server implementation; through some proxies/CDNs requires wss:// tunneling or fallback; no automatic reconnect (client code must handle).
  • Modern use: chat, multiplayer games, collaborative editors, tr traders’ real-time market data, voice/video data channels.

Connection Lifecycle and Heartbeat

Every persistent connection — SSE, WebSocket — must survive idle timeouts at every layer: the client, the user’s home router, the ISP, corporate proxies, the load balancer, the application server. Idle TCP connections over 5–10 minutes silently drop through Identity. Three discipline habits:

  • Heartbeat: send a no-op frame every 10–30 seconds when no real traffic. Upgrades idle to alive.
  • Reconnect on disconnect: both client and server should treat reconnects as ordinary. SSE has this in-browser; WebSocket needs custom code.
  • Sequence numbers / versioning: on reconnect, the client can resume from the last sequence it received, and the server can replay missed messages. Without sequence numbers, a reconnect drops any message that fired during the dead window.

Presence and Fan-out Topologies

A realtime system that pushes to N users needs more than a transport — it needs a topology to route messages.

TopologyShapeWhen it works
DirectEach client connects to one server; server pushes to that clientSingle-server systems (≤ thousands of clients)
Hub / broadcastAll messages go to a central process; it fans out to clientsMedium-scale chat (Slack-like)
Pub/SubClients subscribe to channels; servers publish to channels; a broker dispatchesMulti-tenant chat, multi-room — the canonical realtime pattern
ClusteredEach server holds its own subset of long-polling clients; a backplane (Redis Pub/Sub, NATS, Kafka) routes cross-server messagesHorizontal scale past a single server

The publish/subscribe pattern is the workhorse: clients subscribe("room:42"), anyone publishes publish("room:42", payload), all subscribers on all servers see the payload.

Backpressure in Push Systems

A realtime system can push messages faster than the client can absorb them. Without backpressure, internal queues grow unbounded and the server eventually OOMs. Three patterns:

  • Drop oldest — keep a fixed-size ring buffer; old messages replaced by new. Suitable for telemetry (the latest sample is what matters).
  • Drop all — if the client has fallen behind by N messages, consider this connection dead and disconnect.
  • Coalesce — collapse multiple updates into one. Suitable for presence (“user X is online” — only the latest matters).

The wrong choice manifests as latency blow-up: a slow client’s queue grows, the connection’s latency climbs, and a fresh reconnect is faster than catching up. Most production servers explicitly disconnect slow consumers — the live latency of the user is more valuable than the throughput.

Practice Trajectory

  1. Take an existing realtime flow in a system you know (chat, dashboard, multiplayer). Identify which transport it uses and why. Argue for and against the alternative transport.
  2. Implement an SSE endpoint that pushes a counter every second, with a retry directive on disconnect. Add a named event channel. Observe the browser reconnect.
  3. Implement the same functionality over WebSocket; add a heartbeat and explicit reconnect on drop. Measure the difference in code complexity.
  4. Sketch a fan-out topology for a chat service with 100,000 concurrent users across 50 rooms. Pick the backplane (Redis Pub/Sub, NATS, Kafka); justify.
  5. Design the backpressure rule for a sports-score push service. Pick drop-oldest / drop-all / coalesce; argue why slow clients should disconnect.

When It’s the Right Tool

SituationTakeaway
Server pushes data to client, no client chatterSSE — simplest, most proxy-tolerant; built-in browser reconnect
Bidirectional, low-latency, message-exchangeWebSocket
Server pushes events but only vents to one-shot consumersLong-poll as a fallback through hostile proxies
Fan-out across many serversPub/Sub backplane; servers publish; clients subscribe
Push throughput exceeds client absorptionDefined backpressure policy and explicit disconnect for slow clients