WebSockets: Real-Time Bidirectional Communication

Master WebSocket protocol for real-time bidirectional communication over TCP. Learn handshakes, frames, and building low-latency web applications.

Best viewed on desktop for optimal interactive experience

WebSockets: The Open Phone Line

Like having an always-open phone line rather than sending letters back and forth, WebSockets maintain a persistent, bidirectional channel between client and server. Either side can send messages at any time - no request needed, no waiting.

Speed:
Message Direction:
DisconnectedCLIENTofflineSERVERwaitingWebSocketFrame overhead: 2-14 bytes (vs ~1KB HTTP)

Key Insight: The Open Phone Line

WebSockets establish a single connection and keep it open indefinitely. Both client and server can send messages at any time without waiting for requests. The tiny 2-14 byte frame overhead (vs ~1KB HTTP headers) makes it ideal for high-frequency real-time applications like chat, gaming, and live data feeds.

Key Concepts

  1. Persistent Connection: One-time handshake, then channel stays open indefinitely
  2. Full Duplex: Both client AND server can send messages at any time
  3. Minimal Overhead: 2-14 bytes per frame vs ~1KB HTTP headers
  4. Server Push: Server can initiate communication without client request

The key insight: one connection handles unlimited messages in both directions, with near-zero overhead per message.

How It Works

The Handshake

WebSocket connections start with an HTTP upgrade request:

GET /socket HTTP/1.1 Upgrade: websocket Connection: Upgrade Sec-WebSocket-Key: dGhlIHNhbXBsZSBub25jZQ== HTTP/1.1 101 Switching Protocols Upgrade: websocket Connection: Upgrade

Connection Lifecycle

Client Server | | |--- HTTP Upgrade Request ----->| |<-- 101 Switching Protocols ---| | | |========= WS Connection ========| | | |<------- Message Frame -------->| (bidirectional) |<------- Message Frame -------->| | | |--- Close Frame (1000) -------->| |<-- Close Frame (1000) ---------|

Implementation

import websocket def on_message(ws, message): print(f"Received: {message}") def on_open(ws): ws.send("Hello, Server!") ws = websocket.WebSocketApp( "wss://example.com/socket", on_message=on_message, on_open=on_open ) ws.run_forever()

Performance Characteristics

Overhead Comparison

OverheadHTTP ≈ 1000 \text{ bytes/message}
OverheadWebSocket ≈ 2-14 \text{ bytes/frame}

For 1,000 messages:

  • HTTP Polling: 1,000 × 1KB = 1MB overhead
  • WebSocket: 1,000 × 6B = 6KB overhead
  • Improvement: 170× less overhead

Latency

AspectHTTP PollingWebSocket
Message Latency0-interval~0ms
Connection SetupEvery requestOnce
BidirectionalNoYes

Trade-offs

Advantages

  • True bidirectional communication
  • Minimal per-message overhead
  • Server can push without request
  • Single persistent connection

Limitations

  • Requires WebSocket support (most proxies now support it)
  • More complex than HTTP
  • Connection state to manage
  • Memory per connection (~10-50KB)

When to Use

Perfect For

  • Real-time collaboration - Google Docs, Figma
  • Live updates - Stock tickers, sports scores
  • Gaming - Multiplayer games, game state sync
  • Chat applications - Instant messaging

Not Ideal For

  • Simple request-response - REST APIs, form submissions
  • Cacheable content - Static resources
  • Infrequent updates - Weather apps

If you found this explanation helpful, consider sharing it with others.

Mastodon