Skip to main content

WebSockets

Plain HTTP is request/response: the client asks, the server answers, and the server cannot speak unless spoken to. That is a poor fit for chat, live prices, or multiplayer games, where the server needs to push updates at any moment. WebSocket solves this with a persistent, full-duplex connection where either side may send at will.

Step through the Upgrade handshake, then watch frames fly both ways:

HTTP Upgrade handshakeWS frame (full-duplex)
Client
Server
Step through the Upgrade handshake, then the full-duplex frames.
phase: idlestep 0 / 7

Starting as HTTP: the Upgrade handshake

A WebSocket connection begins life as an ordinary HTTP request. The client sends a GET with an Upgrade: websocket header, asking to switch protocols on this same TCP connection. If the server agrees, it replies with 101 Switching Protocols. From that point on the connection is no longer speaking HTTP - it carries WebSocket frames.

Reusing HTTP for the handshake is deliberate: it travels over the same ports (80 / 443) and passes through the same proxies and firewalls that already allow web traffic.

Full-duplex frames

Once open, both sides exchange lightweight frames independently. The client can send a message while the server is sending one; neither has to wait for a request first. Frames have a tiny header, so per-message overhead is far lower than issuing a fresh HTTP request each time.

HTTP request/responseWebSocket
Who can initiateClient onlyEither side
ConnectionReused, but request-ledPersistent, full-duplex
Server pushNot directlyNative
Per-message overheadFull headersSmall frame header
Good forFetching resourcesLive, bidirectional data
tip

If you only need the server to push occasional updates and never need the client to stream back, simpler options like Server-Sent Events may fit. Reach for WebSocket when you genuinely need both directions to talk freely.

Worked example: overhead of polling vs. a socket

Say a page needs a price update every second for an hour (3,600 updates). Short-polling with HTTP means 3,600 separate requests, each carrying roughly 500-800 bytes of headers (cookies, user-agent, etc.) even though the payload is a few bytes - call it 700 bytes of overhead per poll, or about 2.5 MB of pure header traffic for the hour, plus a fresh TCP-level round trip's worth of latency on every single request.

A WebSocket connection pays that overhead once, during the Upgrade handshake, then sends each update as a frame with as little as 2-6 bytes of framing overhead. The same hour of updates costs roughly 3,600 * 6 bytes = 21.6 KB of framing - about 1% of the polling approach's header cost - and each update arrives the moment the server has it, not on the next poll tick.

Gotcha

A WebSocket connection can die silently while both sides think it is fine. Many corporate proxies, load balancers, and NAT gateways close a TCP connection that has been idle for 30-60 seconds, but neither the client nor the server gets a clean FIN - the socket just goes dark. The client's onclose/onerror handlers may never fire, so the app looks "connected" while no data can flow. The fix is an application-level heartbeat: send a small ping frame every 20-30 seconds (well under the proxy's idle timeout) and reconnect if a pong does not come back in time.