Day 3 — TCP vs UDP, WebSockets, Long Polling, SSE
Goal: Own the transport choices under every network design. By the end you should be able to: - Explain TCP vs UDP — precisely and with trade-offs - Choose the right server-push mechanism (Long Polling / SSE / WebSockets) for a given problem - Understand why real-time systems (chat, gaming, video) pick specific transports - Answer the classic FAANG probe: "how do you push data from server to client?"
Estimated time: 50–60 min read + reflection.
1. Where TCP and UDP sit — quick stack recap
Application: HTTP, HTTPS, WebSocket, gRPC, DNS, SSH, Redis protocol …
Transport: TCP | UDP | QUIC (new, over UDP)
Network: IP
Link: Ethernet / Wi-Fi
The transport layer is what turns "put these bytes in a wire" (IP) into "deliver these bytes to a program on another machine reliably (or not)." Almost everything you build sits on top of TCP or UDP.
2. TCP — Transmission Control Protocol
Contract: "I will deliver your bytes to the other end in order, without gaps, without duplicates — or tell you the connection is dead."
TCP guarantees
- Connection-oriented — a session is established (3-way handshake) and torn down explicitly.
- Reliable delivery — lost segments are retransmitted (uses ACKs + sequence numbers).
- In-order delivery — the receiver reassembles segments in the order sent, even if they arrive out of order.
- Flow control — receiver tells sender "here's my available buffer" via the advertised window, so a fast sender doesn't overwhelm a slow receiver.
- Congestion control — sender backs off when the network is congested (slow-start, AIMD, cubic, BBR). Protects the internet from meltdown.
- Full-duplex — data can flow both directions simultaneously.
The 3-way handshake
Client Server
│─── SYN ─────────────────────────────▶│ "I want to talk, my seq starts at X"
│◀── SYN-ACK ─────────────────────────│ "OK, my seq starts at Y, I ack yours"
│─── ACK ─────────────────────────────▶│ "I ack yours. Let's go."
Cost: 1 RTT before you can send any real data. On a 150 ms cross-continent link, that's a 150 ms hit before you even start.
TCP costs
- Handshake latency (1 RTT).
- Retransmit latency — if a packet drops, the receiver holds up later packets until the missing one arrives. This is head-of-line blocking.
- Larger headers (~20 bytes) vs UDP (~8 bytes).
- Kernel buffer memory per connection (thousands of connections = real RAM).
- Slow start — sender begins with a small congestion window and grows it. Big transfers ramp up; first few packets are the bottleneck.
When TCP is the right choice
- Any workload where byte-for-byte correctness matters and moderate latency is OK: HTTP(S), file transfer, database wire protocols, email, SSH, Git, Redis, Kafka.
- Basically: 99% of general-purpose applications.
3. UDP — User Datagram Protocol
Contract: "I will try to deliver your datagram to the other end. No promises about arrival, order, or duplicates. You handle that yourself if you care."
UDP characteristics
- Connectionless — no handshake, no session state. Each datagram is independent.
- Unreliable — packets can be lost, and no retry happens by the transport.
- Unordered — packets can arrive out of order (or not at all).
- No congestion control — sender can blast at wire speed if the app doesn't self-limit.
- Tiny header (~8 bytes).
- Fire-and-forget — send a datagram, no wait for ACK.
Consequences
- Low latency — no handshake, no ACK wait. You send, it arrives (probably) as fast as the network can carry it.
- Head-of-line blocking is gone — if one datagram is lost, subsequent datagrams still get through and can be used.
- You own reliability if you need it — apps that need "some" reliability build it on top (QUIC does this for HTTP/3).
When UDP is the right choice
- DNS — small queries, retries are cheap, one RTT budget is precious.
- Real-time media (VoIP, video calls, WebRTC) — losing a video frame is fine; retransmitting it 300 ms late is worse than skipping it.
- Online gaming — position updates 60×/sec; a stale packet is useless, drop and use the newer one.
- Time-critical telemetry (metric emission, syslog UDP mode) — better to lose 1% of samples than to block on retries.
- QUIC — Google's transport that gives you HTTP-like reliability and low latency, built over UDP. Basis of HTTP/3.
TCP vs UDP — quick decision matrix
| You need… | Pick |
|---|---|
| Byte-for-byte correctness (files, DB) | TCP |
| Interactive human-timed workloads (RPC, browsing) | TCP |
| Real-time media where stale > missing | UDP |
| Fire-and-forget small messages | UDP |
| Custom reliability on top of low latency | UDP (or QUIC) |
| Massive concurrent clients with minimal state | UDP |
4. The problem HTTP doesn't solve — server push
HTTP is request/response: the client always asks, the server always replies. But many apps need the server to tell the client about something (a new chat message, a stock tick, a live score update, a notification). HTTP alone can't do that without the client asking again.
So the internet has invented four mechanisms to get server → client pushes on top of HTTP:
- Short polling — client asks every N seconds
- Long polling — client asks; server waits until it has something
- Server-Sent Events (SSE) — HTTP-native one-way stream from server to client
- WebSockets — full-duplex, low-overhead, both directions after an HTTP upgrade
Let's take them one by one.
5. Short polling — the naive baseline
Client Server
│─ GET /messages?since=T1 ──▶ │
│◀─ [] ────────────────────── │ (nothing yet)
│ sleep 5 s │
│─ GET /messages?since=T1 ──▶ │
│◀─ [] ────────────────────── │
│ sleep 5 s │
│─ GET /messages?since=T1 ──▶ │
│◀─ [{msg1}] ──────────────── │ (got one!)
- Pros: trivial to implement, works with any HTTP server.
- Cons: wastes requests (99% are empty), latency = polling interval (5 s in the example is 5 s of user-visible lag), scales poorly (N clients × 1 req/interval = big load).
- When acceptable: low-frequency updates where a few seconds' lag is fine (dashboards refreshing every 30 s).
6. Long polling — "hold the connection until you have news"
The client sends a request; the server holds it open (doesn't respond) until either (a) there's an update, or (b) a timeout hits. When either happens, the server responds and the client immediately opens another request.
Client Server
│─ GET /messages ─────────▶ │
│ │ (server waits, no response yet…)
│ │ (still waiting…)
│◀─ {msg1} ─────────────── │ (new message! respond)
│─ GET /messages ─────────▶ │ (client immediately re-connects)
│ │ (waits again…)
- Pros: near-real-time delivery, works over any HTTP infra (proxies, LBs).
- Cons: each server holds many open connections (memory pressure); a new HTTP request per delivery (overhead); no server → client push mid-request (you always wait for the next re-poll cycle).
- When to use: simpler apps where WebSockets are overkill or blocked by corporate firewalls.
7. Server-Sent Events (SSE) — one-way HTTP stream
The client makes one HTTP request; the server responds with Content-Type: text/event-stream and keeps the connection open, writing events as they happen. Built into browsers (EventSource API). It's still HTTP, just… streamed forever.
Client Server
│─ GET /events ──────────▶ │
│◀─ HTTP 200 │
│ Content-Type: text/event-stream│
│ │
│◀─ data: {msg1} ──────────│ (immediately when it happens)
│◀─ data: {msg2} ──────────│
│◀─ data: {msg3} ──────────│
│ … forever ... │
- Pros: HTTP-native (works with LBs, CDNs, HTTP/2 multiplexing); auto-reconnect; simple browser API; low overhead per event (just text); one connection, many events.
- Cons: one-way only (server → client). Client → server still uses a normal HTTP POST. Not supported in some old browsers (mostly irrelevant now).
- When to use: notifications, live feeds, dashboards, activity streams, log tailing, LLM token streaming (ChatGPT-style responses use SSE!).
8. WebSockets — full-duplex, low overhead, real-time
The client makes an HTTP request with Upgrade: websocket. The server responds HTTP 101 Switching Protocols. From then on, the same TCP connection carries WebSocket frames (both directions) — HTTP is out of the picture.
Client Server
│─ GET /ws │
│ Upgrade: websocket ──────▶│
│◀─ HTTP 101 Switching Protocols │
│ ═════════════════════════│ (now WebSocket frames, both ways)
│─ frame: "hi" ─────▶ │
│◀─ frame: "hello" ────── │
│─ frame: "typing…" ─────▶ │
│◀─ frame: {new_msg} ────── │
- Pros: true full-duplex, minimal per-message overhead (~2–14 bytes of framing), low latency (no HTTP round-trip per message), works well for chat / gaming / collaboration / trading.
- Cons: long-lived connections (many open sockets per server); more complex to load-balance (sticky sessions or a routing layer); firewalls/proxies sometimes fight WebSocket upgrades; you own message ordering/reconnection semantics; scaling requires a message bus (Redis Pub/Sub / Kafka) between servers.
- When to use: anything real-time and bi-directional — chat, multiplayer games, live collaboration (Figma, Google Docs), stock/crypto tickers, live betting.
9. Comparison matrix
| Feature | Short Poll | Long Poll | SSE | WebSocket |
|---|---|---|---|---|
| Direction | C↔S | C↔S | S→C only | Full duplex |
| Real-time latency | Poll interval | ~ms | ~ms | ~ms |
| Transport | HTTP | HTTP | HTTP | HTTP → then TCP frames |
| Server resource per client | Low (bursty) | High (open conn) | High (open conn) | High (open conn) |
| Works with existing HTTP infra | ✅ | ✅ | ✅ | Mostly (some proxies fight it) |
| Auto-reconnect built-in | Trivial | Manual | ✅ (EventSource) | Manual |
| Overhead per message | HTTP full | HTTP full | ~50 bytes text | ~2–14 bytes framing |
| Message ordering | Ordered per response | Ordered per response | Ordered | Ordered per connection |
| Backpressure / flow control | HTTP | HTTP | HTTP | Manual (you handle) |
| Great for | Rare updates | Simple push | Broadcasts, feeds | Interactive, bi-di |
10. Design gotchas at scale
10.1 Load-balancing WebSockets
- WebSockets are long-lived, so you can't do simple round-robin per request. Once connected, that client is stuck to that server for the connection's lifetime.
- Sticky sessions (based on client IP or a routing cookie) or a connection router (like a dedicated WebSocket gateway) is common.
- L4 load balancers can pass WebSocket traffic through easily. L7 LBs need explicit WebSocket support (most do — HAProxy, nginx, AWS ALB).
10.2 Cross-server messaging
If a user is connected to server-A and another user (say, in the same chat room) is on server-B, server-A needs a way to tell server-B "deliver this message." Standard pattern:
Client A ─── WS ──▶ Server A ──▶ Pub/Sub (Redis / Kafka) ──▶ Server B ── WS ──▶ Client B
The pub/sub layer decouples app servers so any-to-any messaging works.
10.3 Connection storms after deploys
- Redeploy a WebSocket server → 100k clients reconnect at once → thundering herd.
- Mitigations: connection draining (existing conns finish; new ones go to new servers), staggered restarts, client-side reconnect with exponential backoff + jitter.
10.4 Memory / FD limits
- Each open TCP connection uses kernel memory + a file descriptor.
- Modern Linux can handle hundreds of thousands of concurrent conns per host with tuning (raise
nofileulimit, increasenet.ipv4.tcp_mem, useepoll). - But you'll need a fleet — no single box holds a million WebSockets comfortably.
10.5 Mobile networks eat connections
- Cell networks aggressively kill idle TCP connections. WebSockets and SSE need keepalive pings every ~30–60 s or the connection dies silently.
- On mobile, push notifications (APNs/FCM) are often the right delivery channel, not WebSockets, because they use the OS-managed persistent connection.
11. Interview angle (what FAANG actually asks)
Common flavors:
- "How would you build a chat system?" → WebSockets between clients and servers; Redis/Kafka pub-sub to fan out messages between servers; message store (Cassandra) for history. Discuss sticky sessions or a WebSocket gateway.
- "How would you build live notifications for 100M users?" → SSE for web (or WebSockets), APNs/FCM for mobile background. Fan-out is the interesting problem (Day 18).
- "When would you pick UDP over TCP?" → real-time media, DNS, gaming, telemetry, QUIC. Always tie it back to stale > missing semantics.
- "How does HTTP/3 give better performance than HTTP/2?" → runs on UDP-based QUIC; multiplexes streams without HOL blocking; faster handshake (0-RTT possible on resume); handles connection migration across networks (great for mobile).
- "Difference between SSE and WebSockets?" → SSE is one-way, HTTP-native, auto-reconnecting, simpler. WebSockets are full-duplex, lower overhead per message, better for interactive. Rule of thumb: if the client rarely talks back, SSE. If it's a conversation, WebSockets.
Trap they set: they ask "how would you build a stock ticker?" and if you say "polling" or "WebSockets" without asking "who's the client — web browser, mobile app, or another server?" — you've already lost points. Always clarify the client and the SLA (updates per second, acceptable staleness) before picking a mechanism.
12. Quick reference card (memorize)
- TCP = reliable, ordered, connection-oriented. UDP = fast, unreliable, connectionless.
- UDP wins when stale > missing. Media, gaming, DNS, telemetry.
- TCP head-of-line blocking: one lost packet delays all later data on that connection.
- QUIC/HTTP-3: solves HOL blocking by using UDP + per-stream reliability. Basis of HTTP/3.
- Short poll / long poll / SSE / WebSockets — the four server-push mechanisms.
- SSE = one-way (S→C), HTTP-native, auto-reconnecting. Use for feeds, notifications, LLM token streaming.
- WebSockets = full-duplex, minimal overhead. Use for chat, gaming, collaboration.
- At scale: WebSockets need sticky sessions/routing + pub-sub across servers. Mobile needs keepalive pings or push notifications.
Ready for the Day 3 quiz? Open it in the sidebar (quizzes/day-03.html). Answer in chat when done.