TCP vs UDP — Connection Exhaustion at 5000 Players
TCP's per-connection ~3KB struct exhausted ulimit 1024 and kernel memory at 5000 players.
- TCP guarantees delivery with handshakes, ACKs, and retransmission – but adds latency and head-of-line blocking.
- UDP is connectionless and stateless – one server socket handles unlimited senders with no overhead.
- TCP's 20–60 byte header is larger than UDP's 8 bytes, increasing per-packet cost.
- TCP includes flow control and congestion control; UDP leaves both to the application.
- Production failure: a game server using TCP for 60 updates/second from 1000 players collapses under connection state overhead.
- The real choice: if lost packets corrupt the operation, use TCP; if stale data is worse than no data, use UDP.
Imagine you're sending a birthday card versus shouting across a football field. When you mail a card, the postal service confirms it arrived, resends it if it got lost, and makes sure the pages are in order — that's TCP. When you shout to your friend, you just yell and hope they hear it — no confirmation, no retry — that's UDP. One is careful and reliable; the other is fast and fire-and-forget.
Every time you load a webpage, stream a Netflix show, or jump into an online game, your computer is making a silent but critical decision: should I send this data carefully or as fast as possible? That decision comes down to two protocols — TCP and UDP — and picking the wrong one can mean the difference between a snappy app and one that feels broken. Most developers know the names but can't articulate why one exists alongside the other, which is exactly the knowledge gap that trips people up in system design interviews and production outages alike.
TCP (Transmission Control Protocol) and UDP (User Datagram Protocol) are both built on top of IP, but they solve fundamentally different problems. TCP was designed in 1974 to guarantee that data arrives completely and in order — critical for things like file transfers or HTTP requests where a missing byte corrupts everything. UDP was designed for situations where speed trumps perfection, where a lost packet is better than a late one, and where the application itself can decide how to handle unreliable delivery.
By the end of this article you'll understand the internal mechanics of both protocols, read and run working Java socket examples that show the difference in behaviour, and be able to confidently answer 'which protocol would you use and why?' for any use-case thrown at you — in an interview room or a design document.
How TCP Actually Guarantees Delivery — The Three-Way Handshake and Beyond
TCP's reliability isn't magic — it's engineering. Before a single byte of your data travels anywhere, TCP performs a three-way handshake: the client sends SYN, the server replies SYN-ACK, and the client confirms with ACK. Only then does data flow. This ceremony establishes sequence numbers on both sides, which is how TCP tracks every segment and detects anything that goes missing.
Once connected, every segment the sender transmits must be acknowledged by the receiver. If an ACK doesn't arrive within a timeout window, the segment is retransmitted automatically — your application never sees this retry logic because the OS handles it inside the kernel. TCP also uses flow control (the receiver advertises how much buffer space it has) and congestion control (the sender backs off when the network is overwhelmed). Together, these mechanisms make TCP self-healing but inherently slower.
The cost is latency. Each round trip for a handshake takes time. Each lost packet stalls the entire stream because TCP enforces in-order delivery — a phenomenon called Head-of-Line Blocking. For loading a bank statement or downloading a ZIP file, that's a perfectly acceptable trade-off. For a live video call, it's catastrophic.
Understanding this helps you make smarter architecture decisions. HTTPS runs on TCP because an incomplete HTML response is useless. DNS often uses UDP because a single question-answer fits in one packet and a retry is trivial if it fails.
How UDP Works — Fast, Lightweight, and Deliberately Unreliable
UDP's design philosophy is the opposite of TCP's: get the data out as fast as possible and let the application decide what to do if something goes wrong. There is no handshake, no acknowledgement, no retransmission, and no ordering guarantee. You send a datagram (a self-contained packet) and it either arrives or it doesn't.
This sounds reckless, but it's actually brilliant for certain workloads. Consider a live video stream. A video frame from two seconds ago is worse than useless — it actively hurts the viewer experience. UDP lets the application discard late or missing frames and move on. The same logic applies to DNS lookups (tiny single-packet exchanges), online gaming (position updates become stale in milliseconds), and VoIP calls.
The UDP header is only 8 bytes (source port, destination port, length, checksum) compared to TCP's minimum 20 bytes. Combined with no connection setup overhead, UDP achieves dramatically lower latency. This is why modern protocols like QUIC (used by HTTP/3), DTLS (secure datagrams), and WebRTC are all built on UDP and implement their own selective reliability on top.
Here's the key mental model: UDP is a raw postcard with no tracking. If you need tracking, you add it yourself at the application layer — and only where you need it. That selective reliability is far more efficient than TCP's blanket guarantees.
send() succeeds even if the destination doesn't exist. In production, this means retry logic is your responsibility.Choosing TCP or UDP in the Real World — A Decision Framework
The choice between TCP and UDP isn't about which is 'better' — it's about which constraints match your problem. Run through this mental checklist every time you're designing a networked feature.
Ask: 'What happens if a packet is lost?' If the answer is 'the entire operation is corrupt or meaningless' (file download, database query, user login), use TCP. If the answer is 'the app can recover or the data is stale anyway' (live video frame, game position update, telemetry ping), UDP is worth considering.
Ask: 'How many messages per second?' UDP's stateless nature means a single server socket can handle thousands of different senders without maintaining connection state for each. A gaming server receiving 60 position updates per second from 1,000 players would be crushed by the per-connection overhead of TCP.
Ask: 'Do I need ordered delivery or just fast delivery?' UDP datagrams can arrive out of order. If your application can sequence them itself (most game engines do this with a simple timestamp comparison), you get the speed of UDP with the ordering your game logic needs.
Finally: 'Are you reinventing a solved problem?' If you catch yourself implementing retransmission, acknowledgements, and flow control on top of UDP, you've essentially built a worse version of TCP. Use TCP instead, or use a battle-tested library like KCP or QUIC that already solves this correctly.
TCP Flow Control and Congestion Control — How the Kernel Keeps Things in Check
Beyond delivery guarantees, TCP has two critical mechanisms that UDP lacks entirely: flow control and congestion control. Flow control prevents the sender from overwhelming the receiver's buffer. The receiver advertises a window size (how many bytes it can accept) in every ACK. The sender respects that window — if the window drops to zero, the sender stops until further notice.
Congestion control prevents the sender from overwhelming the network. TCP uses algorithms like Reno, Cubic, or BBR to detect congestion through packet loss or RTT increase. When congestion is detected, TCP cuts its sending rate in half (multiplicative decrease) and slowly ramps back up (additive increase). This AIMD behavior is why TCP traffic is 'polite' — it yields to other flows when the network is busy.
UDP has none of this. A misbehaving UDP application can blast packets at line rate, causing bufferbloat at routers and starving other traffic on the same link. In production, this is why many enterprises rate-limit UDP traffic or enforce QoS policies.
Understanding these mechanisms helps you diagnose production network issues. If your TCP throughput is inconsistent, the culprit is often congestion control kicking in. Look at your TCP retransmission rate and flight size. For UDP, implement application-level pacing and backpressure to avoid flooding the network.
- Flow control = bucket size. Receiver says, 'I can hold 64KB right now.' Sender fills accordingly.
- Congestion control = network pipe diameter. Sender probes the pipe size and reduces flow when it detects backpressure.
- UDP = no bucket, no pipe — you just throw water and hope some gets to the other side.
- When bucket overflows (receiver buffer full), TCP pauses. When UDP overflows, packets drop silently.
Real-World Protocol Selection — Case Studies and Trade-offs
Let's look at concrete examples where the TCP/UDP choice made or broke a system:
Case Study 1: Video Conferencing (Zoom, WebRTC) — Uses UDP almost exclusively. A lost video frame is replaced by the previous frame — invisible to the user. TCP would cause stuttering because every retransmitted frame arrives too late. Audio is even more sensitive: any delay >150ms is noticeable. WebRTC adds FEC (Forward Error Correction) to recover lost packets without retransmission.
Case Study 2: Online Banking — All TCP/HTTPS. Every request must be atomic and complete. A missing byte in a transfer instruction would be catastrophic. The overhead of connection setup is negligible compared to the cost of inconsistency.
Case Study 3: IoT Sensor Telemetry — Most sensors send small readings (temperature, pressure) every few seconds. UDP works well because losing an occasional reading is acceptable. But critical alarms (fire detection) need reliable delivery — those should be sent over TCP or with application-level ACKs.
Case Study 4: DNS — Classic UDP success story. A single query/response fits in 512-4096 bytes. UDP eliminates handshake overhead. If a response is lost, the resolver retries after a timeout — functionally equivalent to TCP's retransmission but without connection state. Large responses (DNSSEC) fall back to TCP.
The pattern: Protocols evolve from TCP to UDP when they hit head-of-line blocking or per-connection scaling limits. HTTP/3's move to QUIC (UDP) is the most visible example.
The 3 AM Blizzard: TCP Connection Exhaustion Behind a Gaming Server
- TCP's per-connection state scales linearly with concurrent connections — not with throughput. For high-frequency, stateless updates, UDP eliminates this scaling tax.
- Always benchmark connection overhead under realistic load before going to production.
- Use kernel tuning (net.core.somaxconn, net.ipv4.tcp_tw_reuse) alongside protocol choice.
send() returns success but receiver never gets the packetsend() only places packet in transmit queue. Use tcpdump on both sides to confirm packet leaves sender and arrives at receiver. If only sender shows it, check firewall/NAT. If receiver shows it but app doesn't see it, check socket buffer overflow with netstat -su.Key takeaways
Common mistakes to avoid
5 patternsDefaulting to TCP for everything because it 'feels safer'
Using the receive buffer length instead of the datagram length when reading UDP data
Assuming send() on a UDP socket throws an exception if the server is unreachable
send() returns successfully even when the destination doesn't exist — reliability is your responsibility, not the protocol's.Neglecting socket buffer sizes for high-throughput UDP pipelines
Using TCP_NODELAY without understanding Nagle's algorithm interaction with delayed ACKs
Interview Questions on This Topic
You're building a multiplayer racing game. Players send their car's position 60 times per second. Would you use TCP or UDP, and why? What would you do about important game events like 'player finished the race'?
Frequently Asked Questions
That's Computer Networks. Mark it forged?
6 min read · try the examples if you haven't