Skip to content

Communication Protocols

The Network Stack

Every HTTPS request flows through three layers:

HTTP   (application layer — your request/response)
TLS    (security layer — encryption + authentication)
TCP    (transport layer — reliable, ordered delivery)

TLS does two things during the handshake:

  1. Authentication — certificate verification against a trusted CA (is this really google.com?)
  2. Encryption — client and server agree on a shared key so all data is encrypted in transit (no ISP/WiFi snooping)

Full connection sequence: TCP handshake → TLS handshake → HTTP request/response


HTTP Versions

HTTP/1.1 — One Request at a Time

A single TCP connection handles requests sequentially — send request, wait for full response, then send the next one. This is head-of-line (HOL) blocking at the HTTP level.

GET /style.css    →  wait...  ← response
GET /image1.png   →  wait...  ← response
GET /image2.png   →  wait...  ← response

Browser workaround: open ~6 parallel TCP connections per domain.

Domain sharding hack: serve resources from images1.example.com, images2.example.com to get 6 × N connections. Real optimization technique from the HTTP/1.1 era.

HTTP/2 — Multiplexing

One TCP connection carries multiple streams simultaneously. Data is broken into small frames tagged with a stream ID. Client reassembles by stream ID.

Connection:  [css-frame][img-frame][js-frame][css-frame][img-frame]...
Stream 1:    css -----> css -----> css (done)
Stream 2:    img -----> img -----> img -----> img (done)
Stream 3:    js ------> js (done)

No more HOL blocking at the HTTP level. No need for 6 connections or domain sharding.

But: still runs on TCP. If a single TCP packet is lost, all streams are blocked waiting for retransmit. HOL blocking moved down one layer.

HTTP/3 — QUIC over UDP

Drops TCP entirely. Uses QUIC — a protocol built on UDP that adds reliability per stream.

  • HTTP/2 (TCP): one lost packet → all streams blocked
  • HTTP/3 (QUIC): one lost packet → only that stream blocked, others keep flowing

Bonus: QUIC combines TCP + TLS handshake into a single handshake — faster first request.

Comparison

HTTP Version Transport HOL Blocking?
HTTP/1.1 TCP Yes — HTTP level (one request at a time)
HTTP/2 TCP Solved at HTTP level, still exists at TCP level
HTTP/3 QUIC (over UDP) No — independent streams at transport level

Communication Patterns

From simplest to most capable:

One-shot            →  HTTP request/response
Client keeps asking →  Short polling
Server holds open   →  Long polling
Server streams      →  SSE (one-way)
Full duplex         →  WebSockets (two-way)

HTTP Request/Response

Client asks, server answers, done. Simple and stateless.

Short Polling

Client asks "anything new?" repeatedly on a timer. Server responds immediately (yes or no). Simple but wasteful — most responses are empty.

Long Polling

Client sends request. Server holds the connection open until it has data, then responds. Client immediately reconnects.

Tradeoff vs SSE: every response = tear down + reconnect. For frequent updates (e.g., stock ticker updating every second), this means a new HTTP request per update. Works everywhere though (just regular HTTP).

Server-Sent Events (SSE)

Client sends one HTTP request. Server responds with Content-Type: text/event-stream and never closes the response. Keeps streaming data as it becomes available.

  • One direction only — server → client
  • Connection stays open — no reconnection overhead
  • ChatGPT uses SSE for token-by-token streaming

SSE > Long Polling when updates are frequent (avoids reconnection overhead). Long Polling > SSE when updates are rare or SSE isn't supported (older browsers, corporate proxies).

WebSockets

sequenceDiagram
    participant Client
    participant Server

    Client->>Server: GET /chat HTTP/1.1<br/>Upgrade: websocket
    Server-->>Client: HTTP/1.1 101 Switching Protocols<br/>Upgrade: websocket
    Note over Client,Server: TCP connection is now a WebSocket
    Client->>Server: message frame
    Server->>Client: message frame
    Server->>Client: message frame
    Client->>Server: message frame
    Note over Client,Server: Either side can send at any time

Starts as HTTP (passes through firewalls/proxies that understand HTTP), then upgrades — after the 101 response, the connection is no longer HTTP. It's a raw bidirectional TCP pipe where either side can send frames at any time.

Why SSE can't replace WebSockets: SSE is still an HTTP response stream. HTTP is fundamentally request → response. The client's side is "done" after the initial request — no mechanism to send data back on that stream. WebSockets ditch HTTP entirely after the upgrade.


Choosing the Right Protocol

Use Case Protocol Why
Live sports scores SSE Server pushes updates, client just receives
Stock price ticker SSE Same — one-way stream of updates
ChatGPT streaming SSE Server streams tokens, client just reads
Chat (Slack, WhatsApp) WebSockets Bidirectional — users send AND receive
Uber rider app WebSockets Need both location updates (receive) AND messaging (send + receive)

Key insight: once you already need WebSockets for one feature (e.g., messaging), route everything through it rather than maintaining two connection types. Use a type field in payloads to differentiate:

{"type": "location_update", "data": {"lat": 37.7, "lng": -122.4}}
{"type": "message", "data": {"from": "driver", "text": "I'm outside"}}

The WebSocket server acts as a gateway — holds the client connection and fans out to/from separate backend services (location service, messaging service, etc.). Services stay decoupled; the WS gateway multiplexes everything down one pipe to the client.

graph LR
    R[Rider Client] <-->|Single WS Connection| GW[WS Gateway]
    GW <--> LS[Location Service]
    GW <--> MS[Messaging Service]