SDE Path

TCP vs UDP: Reliability, Flow Control, and Congestion Control

15 min read

TCP and UDP are the two workhorse transport protocols. They sit at layer 4 and both use ports to deliver data to the right process, but they make opposite trade-offs. TCP is a reliable, ordered, connection-oriented byte stream. UDP is an unreliable, connectionless datagram service. Knowing exactly which guarantees each gives — and how TCP enforces its guarantees — is one of the most reliably asked systems questions.

The one-paragraph summary

TCP guarantees that bytes arrive in order, exactly once, with no duplicates or corruption, and it slows itself down when the network is congested. You pay for this with connection setup latency, per-connection state, and head-of-line blocking. UDP just fires datagrams and forgets them: no handshake, no ordering, no retransmission. You get minimal latency and overhead, and you build any reliability you need yourself.

| Property | TCP | UDP | |----------|-----|-----| | Connection | Connection-oriented (handshake) | Connectionless | | Reliability | Guaranteed delivery, retransmits | Best-effort, no retransmit | | Ordering | In-order byte stream | No ordering | | Flow control | Yes (receiver window) | No | | Congestion control | Yes | No | | Header size | 20 bytes (min) | 8 bytes | | Boundaries | Byte stream (no message boundaries) | Preserves message boundaries | | Use cases | HTTP, SSH, database, email | DNS, VoIP, video, gaming, QUIC |

The three-way handshake

TCP is connection-oriented: before any data flows, both sides synchronize sequence numbers with a three-way handshake (SYN, SYN-ACK, ACK). Sequence numbers let TCP detect loss, reorder segments, and discard duplicates. Teardown uses a four-way exchange of FIN/ACK.

Step through the handshake and teardown below. Watch the SYN and ACK flags and how each side's initial sequence number (ISN) advances:

TCP handshake & teardown

Step through connection setup and close, seq/ack and all.

Client

CLOSED

Server

LISTEN

ClientServer
0 / 7
Ready to connect

Client is CLOSED, server is LISTEN (passive open). Press Next to send the first SYN.

SYN and FIN each consume one sequence number; a pure ACK does not.

The exchange, spelled out:

Client → Server:  SYN,  seq=1000
Server → Client:  SYN, ACK,  seq=5000, ack=1001
Client → Server:  ACK,  seq=1001, ack=5001
   ... data flows ...
Client → Server:  FIN,  seq=x
Server → Client:  ACK,  ack=x+1
Server → Client:  FIN,  seq=y
Client → Server:  ACK,  ack=y+1   (then TIME_WAIT)

Why three messages and not two? Both sides must confirm that the other side's sequence number was received. The client's ISN is acknowledged by the SYN-ACK; the server's ISN is acknowledged by the final ACK. Two messages would leave one direction unconfirmed.

TIME_WAIT is a common follow-up: after closing, the initiator waits ~2×MSL so that stray delayed segments from the old connection cannot be misread by a new connection reusing the same 4-tuple.

Reliability mechanics

TCP achieves reliable delivery with a small set of cooperating mechanisms:

  • Sequence numbers label every byte so the receiver can reorder and deduplicate.
  • Cumulative ACKs tell the sender the highest contiguous byte received. Selective ACK (SACK) can additionally report out-of-order blocks.
  • Retransmission on timeout (RTO) or on three duplicate ACKs (fast retransmit) resends lost data without waiting for the full timeout.
  • Checksums detect corruption so damaged segments are dropped and retransmitted.

Flow control vs congestion control

These two are frequently confused. Keep them distinct:

  • Flow control protects the receiver from being overwhelmed. The receiver advertises a receive window (rwnd) in every ACK — "I have this much buffer free." The sender never has more unacknowledged data in flight than the window allows. A window of zero pauses the sender until the receiver opens it again.
  • Congestion control protects the network from being overwhelmed. The sender maintains a congestion window (cwnd) that it grows and shrinks based on inferred network conditions. The amount the sender may transmit is min(rwnd, cwnd).

Congestion control phases

Slow start:            cwnd doubles each RTT (exponential) until ssthresh
Congestion avoidance:  cwnd grows by ~1 MSS per RTT (linear, additive increase)
Loss (3 dup ACKs):     fast retransmit + fast recovery; cwnd halved (multiplicative decrease)
Loss (timeout):        cwnd reset to 1 MSS, back to slow start

This AIMD (additive-increase / multiplicative-decrease) behavior is what makes TCP "fair" — competing flows converge toward an equal share of a bottleneck link. Modern variants like CUBIC (Linux default) and BBR (models bandwidth and RTT directly) improve on classic Reno on high-bandwidth, high-latency links.

When to choose UDP

UDP wins whenever timeliness beats completeness or you can add reliability selectively:

  • DNS — a single tiny request/response; a lost query is just retried.
  • VoIP / video / gaming — a late packet is worse than a lost one; drop it and move on.
  • QUIC / HTTP/3 — runs over UDP and rebuilds ordering, reliability, and congestion control in user space to avoid TCP's head-of-line blocking and to enable 0-RTT connection setup.

Head-of-line blocking is the crucial motivator for QUIC: in TCP, one lost segment stalls delivery of all later bytes (even from independent HTTP/2 streams) until it is retransmitted. QUIC multiplexes independent streams so a loss on one does not block the others.

Common gotchas

  • UDP has checksums too (optional in IPv4, mandatory in IPv6) — "no reliability" means no retransmission and no ordering, not zero integrity checking.
  • TCP has no message boundaries. Two send() calls can arrive as one recv(), or split across several. You must frame messages yourself (length prefix or delimiter).
  • A TCP connection is identified by a 4-tuple: (src IP, src port, dst IP, dst port). The same server port serves thousands of clients because the client side differs.

In the interview

  1. Lead with the trade-off: reliability + ordering vs latency + simplicity.
  2. Explain the handshake and why it is three messages, plus TIME_WAIT if pushed.
  3. Separate flow control (receiver, rwnd) from congestion control (network, cwnd) — this distinction is a frequent gotcha.
  4. Cite QUIC/HTTP/3 to show you know where the industry is heading and why (head-of-line blocking, 0-RTT).

Check yourself

5 questions — graded when you submit.

Question 1

What is the primary purpose of TCP's three-way handshake?

Question 2

What is the key difference between TCP flow control and congestion control?

Question 3

Which scenario is the strongest fit for UDP rather than TCP?

Question 4

In TCP congestion control, what happens to the congestion window (cwnd) during the 'slow start' phase?

Question 5

Why does HTTP/3 (QUIC) run over UDP instead of TCP?

Finished reading?

Sign in to save your progress across lessons.