In one line: in every ACK, the receiver tells the sender "I can accept N more bytes" (receive window, rwnd). The sender never exceeds rwnd — making receiver-buffer overflow structurally impossible.
What it is#
send window = min(rwnd, cwnd)
↑ ↑
receiver capacity sender's network estimate
The sender maintains a sliding window with four regions: acked / sent-but-unacked / sendable / not-yet-allowed. Each ACK slides it right.
Analogy#
You're delivering buckets of water. Your friend says "I have room for 5 more" (rwnd=5), so you send 5. After they drink 3, they say "room for 3 more" (rwnd=3), and you continue. You never deliver more than the kitchen can hold.
Key concepts#
How it works#
The slower the receiver app reads, the smaller rwnd shrinks, and the sender slows down accordingly.
Practical notes#
-
Modern OSes enable Window Scaling by default — required for transoceanic "long fat pipes" (high BDP).
-
Tune server receive buffers:
sysctl -w net.core.rmem_max=16777216 sysctl -w net.ipv4.tcp_rmem='4096 87380 16777216'Too-small buffers cap rwnd and starve the wire.
-
Slow
read()in the app keeps rwnd at 0 for long periods, stalling the sender — looks like "packet loss" but is really "slow reader". -
Disable Nagle (
TCP_NODELAY) for latency-sensitive small writes (IM, games, interactive SSH). -
Don't conflate rwnd with cwnd: rwnd is receiver capacity, cwnd is network capacity — the sender takes the smaller of the two.
Easy confusions#
Fix: bigger buffer / faster reader.
Fix: better link / different congestion algo (e.g. BBR).