TCP vs UDP
The Complete Comparison
LinkedIn Hook
"Should I use TCP or UDP?"
This is one of those interview questions that sounds simple — and then quietly reveals how deeply you understand distributed systems.
The naive answer: "Use TCP for reliability, UDP for speed."
The real answer is about tradeoffs:
- A video call that pauses and stutters is worse than one with a tiny audio glitch
- A database that delivers data out of order is worse than one that is slightly slower
- An HTTP/3 connection that uses UDP is not unreliable — it just implements reliability differently
The choice between TCP and UDP is not about reliability vs speed. It is about where the reliability logic lives and which latency tradeoffs your application can tolerate.
This lesson breaks down every dimension of the comparison — with a decision framework you can use in interviews and in production.
Full lesson → [link]
#Networking #TCP #UDP #SystemDesign #BackendEngineering #InterviewPrep
What You'll Learn
- The complete side-by-side comparison of TCP and UDP across every dimension
- The mental model for choosing between them: where does reliability logic belong?
- Real-world use cases with the reasoning behind each choice
- What happens when a protocol "uses UDP" but adds reliability on top (QUIC, RTP, DTLS)
- Common interview scenarios and how to answer the TCP vs UDP question
The Core Mental Model
Before the comparison table, the key insight:
TCP puts reliability inside the transport layer. The operating system's network stack handles retransmission, ordering, flow control, and congestion control. Your application code sees a byte stream — perfectly ordered, no gaps.
UDP puts reliability decisions in the application layer. Your application receives raw datagrams — possibly lost, possibly out of order. If the application needs reliability, it implements exactly the kind it needs.
This is why the question "TCP or UDP?" is really the question: "Who should implement reliability — the OS or the application?"
The Complete Comparison
| Dimension | TCP | UDP |
|---|---|---|
| Connection | Required — 3-way handshake before data | None — first packet IS the data |
| Reliability | Guaranteed — ACKs, retransmission | None — lost packets are gone |
| Ordering | Guaranteed — sequence numbers reorder | None — packets may arrive out of order |
| Speed | Slower — overhead of ACKs, handshake, windowing | Faster — minimal overhead |
| Header size | 20–60 bytes (minimum 20) | 8 bytes (fixed) |
| Flow control | Yes — receiver window (rwnd) limits sender | None |
| Congestion control | Yes — slow start, AIMD | None (app's responsibility) |
| Error detection | Yes — checksum (mandatory) | Yes — checksum (optional in IPv4) |
| Error correction | Yes — retransmit corrupted/lost segments | None — bad datagrams are dropped |
| Delivery model | Stream (continuous byte flow, no message boundaries) | Datagram (each packet is a discrete message) |
| Broadcast/Multicast | No — point-to-point only | Yes — supports broadcast and multicast |
| State | Stateful — both sides maintain connection state | Stateless — no connection state |
| Teardown | Required — 4-way FIN handshake | None |
| Resource usage | Higher — buffers, timers, state machines | Lower — nearly zero per-datagram overhead |
Header Comparison
TCP HEADER (minimum 20 bytes)
────────────────────────────────────────────────────────────────
[ Source Port 2B ][ Destination Port 2B ]
[ Sequence Number 4B ]
[ Acknowledgement Number 4B ]
[ Data Offset 4b ][ Reserved 4b ][ Flags 8b ][ Window Size 2B ]
[ Checksum 2B ][ Urgent Pointer 2B ]
[ Options (variable, 0–40B) ]
UDP HEADER (fixed 8 bytes)
────────────────────────────────────────────────────────────────
[ Source Port 2B ][ Destination Port 2B ]
[ Length 2B ][ Checksum 2B ]
TCP uses 2.5× more header space at minimum — for every small packet, this overhead is significant.
Use Case Decision Framework
Choose TCP when:
Data integrity is non-negotiable.
- Web browsing (HTTP/HTTPS) — a corrupt HTML file produces a broken page
- Database queries — a lost byte in a SQL response could return wrong results silently
- File transfer (FTP, SFTP, S3) — a partially downloaded binary file is unusable
- Email (SMTP, IMAP) — a missing email is worse than a slightly delayed one
- SSH sessions — a dropped command character could execute the wrong command
Data must be ordered.
- Any streaming where the consumer processes data in sequence (log streams, database replication)
- API responses — JSON with missing bytes is not parseable
You want the OS to handle retransmission logic.
- You do not want to implement retry/ACK logic in your application
Choose UDP when:
Latency matters more than completeness.
- Live video/audio calls — a retransmitted frame arrives too late to be useful
- Online gaming — a lost position update is better than a delayed one (player would have moved by then)
- Real-time sensor data — old readings are irrelevant when new ones are arriving
You need single-packet exchanges.
- DNS — one packet query, one packet response; retry at application level is trivial
- DHCP — device has no IP yet; TCP is impossible before having an IP
- NTP — time sync is a single round-trip
You need broadcast or multicast.
- Service discovery (mDNS/Bonjour) — one packet broadcast to all devices on the LAN
- IPTV — one stream delivered to many receivers simultaneously
You want to implement custom reliability.
- QUIC (HTTP/3) — UDP + TLS 1.3 + custom stream multiplexing + per-stream retransmission
- RTP — UDP + sequence numbers + timestamps for A/V sync
- Custom game protocols — UDP + delta compression + selective ACK for priority data
Real-World Examples and Reasoning
HTTP/1.1 and HTTP/2 → TCP
HTTP is stateful in practice: cookies, sessions, request/response pairing. The response must be complete and ordered — a web page with half the HTML is not a web page. TCP provides this at zero application cost.
HTTP/3 → UDP (via QUIC)
HTTP/2 over TCP suffers from head-of-line blocking — if one TCP segment is lost, all HTTP/2 streams on that connection stall waiting for the retransmit. HTTP/3 uses QUIC (UDP-based): each HTTP/3 stream is independent, so a lost packet in stream A does not block stream B. On lossy mobile networks, HTTP/3 can be significantly faster than HTTP/2.
DNS → UDP (with TCP fallback)
DNS queries typically fit in one datagram. UDP means zero setup cost. If the DNS response exceeds the UDP limit (512 bytes, or 4096 with EDNS), or for zone transfers, DNS falls back to TCP.
Video Conferencing (Zoom, Google Meet) → UDP
A video frame 200ms old is useless. When TCP retransmits, it stalls all subsequent frames waiting for the missing one — causing visible freezes. With UDP, a lost frame produces a brief glitch and the video moves on. Zoom's transport protocol is UDP-based with its own RTP/SRTP layer for encryption and sequence tracking.
Online Gaming → UDP
A player's position from 100ms ago is stale — the player has moved. Games send position updates at 64 Hz (every 15ms). They use UDP and implement client-side prediction and server reconciliation. Only truly important events (item picked up, damage dealt) may use reliability mechanisms — and only for those packets.
SSH → TCP
Every character typed in an SSH terminal must arrive in order without loss. A dropped character executing rm -rf / at position 8 instead of /tmp/ would be catastrophic. TCP is the obvious choice.
Code Example 1 — Comparing Overhead
// Illustrates the protocol overhead difference with a realistic small payload
function analyzeOverhead(protocolName, headerBytes, payloadBytes) {
const totalBytes = headerBytes + payloadBytes;
const overheadPct = ((headerBytes / totalBytes) * 100).toFixed(1);
const efficiency = ((payloadBytes / totalBytes) * 100).toFixed(1);
console.log(`\n${protocolName}`);
console.log(` Header: ${headerBytes} bytes`);
console.log(` Payload: ${payloadBytes} bytes`);
console.log(` Total: ${totalBytes} bytes`);
console.log(` Overhead: ${overheadPct}%`);
console.log(` Efficiency: ${efficiency}%`);
}
// DNS query — a real small UDP datagram
analyzeOverhead("DNS query (UDP)", 8, 29); // 8B UDP header + ~29B DNS payload
analyzeOverhead("DNS query (TCP)", 20, 29); // 20B TCP header + ~29B DNS payload (+ 3 handshake packets)
// HTTP request — typical small TCP segment
analyzeOverhead("HTTP GET (TCP)", 20, 100); // 20B TCP header + 100B HTTP headers
analyzeOverhead("HTTP GET (UDP)", 8, 100); // Hypothetical — lower overhead per segment
// Large file transfer — overhead becomes negligible
analyzeOverhead("File chunk (TCP)", 20, 1460); // 1460B is typical TCP MSS (max segment size)
analyzeOverhead("File chunk (UDP)", 8, 1472); // 1472B is typical UDP max before fragmentation
// Output shows:
// For small payloads (DNS): UDP saves 12 bytes — meaningful when millions of queries/sec
// For large payloads (file): header overhead drops below 1.4% — protocol choice matters less
Code Example 2 — TCP vs UDP Decision Simulator
// Given an application's requirements, recommend TCP or UDP
function recommendProtocol(requirements) {
const {
name,
latencySensitive, // true = ms matter; false = correctness matters
toleratesLoss, // true = ok to drop some data; false = need all bytes
needsOrdering, // true = order matters; false = ok to receive out of order
messageSize, // "small" | "large"
needsBroadcast, // true = one-to-many; false = point-to-point
customReliability, // true = application will implement own reliability
} = requirements;
const reasons = [];
let tcpScore = 0;
let udpScore = 0;
if (!toleratesLoss) { tcpScore += 3; reasons.push("Loss intolerance → TCP (guaranteed delivery)"); }
if (!needsOrdering) { udpScore += 2; reasons.push("Order not required → UDP (no reordering overhead)"); }
if (latencySensitive) { udpScore += 2; reasons.push("Latency-sensitive → UDP (no handshake)"); }
if (!latencySensitive) { tcpScore += 1; reasons.push("Latency-tolerant → TCP fine"); }
if (needsBroadcast) { udpScore += 3; reasons.push("Broadcast/multicast required → UDP only"); }
if (messageSize === "small") { udpScore += 1; reasons.push("Small messages → UDP header overhead matters"); }
if (customReliability) { udpScore += 2; reasons.push("Custom reliability → UDP (control your own logic)"); }
if (toleratesLoss && !customReliability) { udpScore += 1; reasons.push("Tolerates loss with no custom logic → UDP simplest"); }
const recommendation = tcpScore >= udpScore ? "TCP" : "UDP";
const confidence = Math.abs(tcpScore - udpScore) > 3 ? "strong" : "marginal";
console.log(`\n=== ${name} ===`);
reasons.forEach(r => console.log(` • ${r}`));
console.log(` TCP score: ${tcpScore} | UDP score: ${udpScore}`);
console.log(` Recommendation: ${recommendation} (${confidence})`);
}
const apps = [
{
name: "Database query (PostgreSQL)",
latencySensitive: false, toleratesLoss: false,
needsOrdering: true, messageSize: "large",
needsBroadcast: false, customReliability: false,
},
{
name: "Video call (WebRTC)",
latencySensitive: true, toleratesLoss: true,
needsOrdering: false, messageSize: "small",
needsBroadcast: false, customReliability: true,
},
{
name: "DNS resolution",
latencySensitive: true, toleratesLoss: true,
needsOrdering: false, messageSize: "small",
needsBroadcast: false, customReliability: false,
},
{
name: "File download (HTTPS)",
latencySensitive: false, toleratesLoss: false,
needsOrdering: true, messageSize: "large",
needsBroadcast: false, customReliability: false,
},
{
name: "LAN service discovery",
latencySensitive: true, toleratesLoss: true,
needsOrdering: false, messageSize: "small",
needsBroadcast: true, customReliability: false,
},
];
apps.forEach(recommendProtocol);
Common Mistakes
Mistake 1 — Equating "UDP" with "unreliable application"
UDP is unreliable at the transport layer. But QUIC — which powers HTTP/3 and Cloudflare's entire edge — is UDP-based and is more reliable in practice than HTTP/2 over TCP on lossy networks. The protocol being UDP does not mean the application is unreliable; it means reliability is implemented where the application needs it. Confusing transport reliability with application reliability is a fundamental mistake.
Mistake 2 — Defaulting to TCP for everything because it is "safe"
TCP's guarantees have costs: handshake latency, head-of-line blocking, flow control, congestion control. For an internal microservice making millions of small status check calls per second, TCP connection overhead can become a real bottleneck. Sometimes UDP with application-level retry (or just accepting occasional loss) is genuinely the right engineering decision.
Mistake 3 — Not knowing that UDP can fall back to TCP
DNS is primarily UDP but falls back to TCP for large responses. SIP (VoIP signaling) uses UDP but can use TCP. Knowing that protocols can switch between transports based on conditions — and why — demonstrates deep understanding in interviews.
Interview Questions
Q: You are building a multiplayer FPS game. Would you use TCP or UDP? Why?
UDP. Player position and game state updates are sent at 64 Hz (every 15ms). A position update from 15ms ago is already stale — retransmitting it via TCP would cause the game to stall waiting for a packet whose information is no longer useful. With UDP, lost updates are simply skipped; the client interpolates using the last known state. For critical game events (health changes, item pickups), the game engine implements selective acknowledgement directly on top of UDP.
Q: HTTP/3 uses UDP. Does that mean HTTP/3 is less reliable than HTTP/2?
No. HTTP/3 runs over QUIC, which is built on UDP but implements its own reliability, encryption (TLS 1.3), and stream multiplexing. QUIC is actually more reliable on lossy networks because it eliminates TCP's head-of-line blocking: a lost packet in one HTTP/3 stream does not stall other streams on the same connection, whereas in HTTP/2 over TCP, one lost segment stalls all multiplexed streams.
Q: What is the difference between TCP's byte stream and UDP's datagram model?
TCP presents a continuous byte stream to the application — there are no message boundaries in the stream itself. If you send() two messages, the receiver might recv() them as one combined message or split across multiple reads. UDP preserves datagram boundaries — each sendto() produces exactly one datagram, and each recvfrom() delivers exactly one datagram. Application protocols on TCP must implement their own framing (e.g., HTTP uses Content-Length or chunked encoding to know where one response ends).
Q: When would you prefer TCP over UDP even if speed is important?
When data integrity is non-negotiable regardless of speed. A database query, file transfer, or authentication token must arrive complete and correct — a partially received or corrupted result is worse than a slightly delayed correct one. For these cases, the overhead of TCP's reliability mechanisms is less costly than the bugs introduced by missing or corrupted data.
Quick Reference — Cheat Sheet
| TCP | UDP | |
|---|---|---|
| Connection | Required (3-way handshake) | None |
| Reliability | Guaranteed (ACK + retransmit) | None |
| Ordering | Guaranteed | None |
| Speed | Slower | Faster |
| Header | 20–60 bytes | 8 bytes |
| Flow control | Yes (rwnd) | No |
| Congestion ctrl | Yes (cwnd, slow start) | No |
| Broadcast | No | Yes |
| Delivery model | Byte stream | Datagrams |
Use TCP for: HTTP, HTTPS, SSH, databases, email, file transfer, anything where correctness > speed
Use UDP for: DNS, VoIP, gaming, video calls, NTP, DHCP, QUIC, service discovery, real-time telemetry
The key question: Who should implement reliability — the OS network stack or my application?
- OS (TCP): simpler app code, more overhead, head-of-line blocking risk
- Application (UDP): more app code, less overhead, fine-grained control
Previous: Lesson 4.2 — UDP — Fast Communication → Next: Lesson 4.4 — Ports & Sockets →
This is Lesson 4.3 of the Networking Interview Prep Course — 8 chapters, 32 lessons.