WebSockets & Real-Time Protocols

WebSocket upgrade handshake, full-duplex frames, ping/pong heartbeat, and comparison with SSE and long-polling.

Overview

Overview Real-time communication between clients and servers is a fundamental requirement for chat applications, live dashboards, collaborative editing, trading platforms, and online games. HTTP was designed as a request/response protocol — the client always initiates. WebSockets, Server-Sent Events (SSE), and long-polling are three approaches that work around or extend HTTP to enable server-initiated data delivery.

WebSockets (RFC 6455) provide a full-duplex, persistent, bidirectional communication channel over a single TCP connection. The protocol begins with an HTTP upgrade handshake: the client sends an HTTP/1.1 Upgrade request with specific WebSocket headers (Upgrade: websocket, Connection: Upgrade, Sec-WebSocket-Key), and the server responds with 101 Switching Protocols. After the upgrade, the connection is no longer HTTP — data flows as WebSocket frames, which have a compact binary header (2-10 bytes) followed by payload. Either side can send frames at any time, and either side can initiate close.

Server-Sent Events (SSE) is a simpler alternative for one-way server-to-client streaming. The server sends text/event-stream content over a persistent HTTP connection. SSE has built-in reconnection (the browser automatically reconnects with Last-Event-ID), works over HTTP/2 (where WebSockets do not), and is trivially proxied by any HTTP infrastructure. Its limitation is that it is unidirectional — clients can only receive, not send over the SSE stream.

Long-polling is the oldest technique and works on any HTTP infrastructure. The client sends a request; the server holds it open until data is available (or a timeout), then responds. The client immediately re-sends. Long-polling has high overhead (HTTP headers on every message) and is largely replaced by WebSockets and SSE for new development.

For production systems, WebSocket scaling requires sticky sessions or a shared pub/sub backend (Redis, Kafka) since WebSocket connections are stateful. Understanding ping/pong heartbeats, frame types, masking (required for client-to-server frames by the RFC), and graceful closure is essential for building reliable real-time infrastructure.

Key concepts

WebSocket Upgrade Handshake
WebSocket connections start as HTTP/1.1 requests with Upgrade: websocket and Connection: Upgrade headers. The client sends a random 16-byte base64-encoded nonce in Sec-WebSocket-Key. The server concatenates the key with a magic GUID, SHA-1 hashes the result, base64-encodes it, and returns it in Sec-WebSocket-Accept. This proves the server genuinely understands the WebSocket protocol. After the 101 response, the TCP connection switches protocols.
WebSocket Frames
WebSocket data is carried in frames. Each frame has a 2-byte minimum header: FIN bit (last frame of a message), opcode (0=continuation, 1=text, 2=binary, 8=close, 9=ping, 10=pong), MASK bit, and payload length. Client-to-server frames must be masked with a 4-byte masking key XOR'd against the payload. Server-to-client frames must NOT be masked. Masking prevents cache poisoning attacks against proxies that might cache WebSocket frames as HTTP responses.
Ping/Pong Heartbeat
Either side can send a Ping frame (opcode 9) with optional payload up to 125 bytes. The recipient must respond with a Pong frame (opcode 10) containing the same payload. Ping/pong is used to detect dead connections when no application data flows and to keep NAT bindings and load balancer connections alive. Many load balancers (nginx, ALB) close idle WebSocket connections after a configurable timeout; heartbeats prevent this.
Server-Sent Events (SSE)
A W3C standard for server-to-client streaming over HTTP. The server sets Content-Type: text/event-stream and sends events in the format "data: payload ". SSE supports named events, event IDs for resumption, and retry intervals. Browser EventSource API handles automatic reconnection with the Last-Event-ID header. SSE works over HTTP/2 (multiple SSE streams can share one connection), unlike WebSockets which require HTTP/1.1 for the upgrade.
Sticky Sessions for WebSockets
WebSocket connections are long-lived and stateful — in-memory state (subscriptions, cursors, presence) lives on the specific server handling the connection. A load balancer must route all requests from the same client to the same backend server. This is done via cookie-based sticky sessions (AWS ALB: AWSALB cookie) or IP hash routing. The alternative is to externalize all state to a shared store (Redis pub/sub, Kafka consumer groups) so any server can handle any connection.

When to use it

  • Building a chat application, collaborative editor, or live dashboard requiring bidirectional real-time communication
  • Choosing between WebSockets and SSE for a server-push notification system
  • Debugging disconnecting WebSocket connections behind a load balancer or proxy
  • Designing WebSocket scalability with sticky sessions or a pub/sub fan-out backend
  • Implementing heartbeat probes to detect and recover from dead WebSocket connections

Interview questions

  • How does the WebSocket upgrade handshake work, and how would you debug a WebSocket connection that fails to establish?
  • Compare WebSockets, Server-Sent Events, and long-polling. When would you use each?

Sign up free to read the full topic

More Networking topics

Start free on Cariara · Coding problems · System design studio