4.3.21 · D2Computer Networks

Visual walkthrough — TCP flow control — sliding window, receive buffer

2,044 words9 min readBack to topic

We will use only counting and subtraction. Every number below is a byte number — a position on an imaginary tape of data, like page numbers in a very long book. Nothing more advanced than "this number minus that number" appears anywhere.


Step 1 — Draw the box before drawing anything in it

WHAT. The receiver owns a fixed chunk of memory. Think of it as a box with a fixed width. We call that width ==RcvBuffer== — measured in bytes, the same unit as everything on this page.

WHY start here. Every later quantity is a distance measured inside this box. If we do not fix the box first, "how full is it?" and "how much room is left?" have no meaning. The box is the ruler.

PICTURE. Below: an empty box of width RcvBuffer. The left wall is where the oldest un-read byte would sit; the right wall is the hard limit. Nothing is inside yet — occupancy is zero, free space is the whole width.

Figure — TCP flow control — sliding window, receive buffer

Step 2 — Two markers on the tape: what arrived, what got read

WHAT. Data does not vanish the instant it arrives — it waits in the box until the application (the program using TCP) picks it up. So we track two positions on the byte-tape:

  • ==LastByteRcvd== — the highest byte number TCP has accepted into the box.
  • ==LastByteRead== — the highest byte number the application has carried out of the box.

WHY these two and not others. We want to know how full the box is. Fullness is "stuff that came in but has not left yet." LastByteRcvd is the in-arrow; LastByteRead is the out-arrow. The gap between them is exactly the stuff still sitting inside. No third marker is needed.

PICTURE. The shaded amber band between the two markers is the occupied region. Notice the band sits at the left of the box — read bytes have already exited, so the used region starts where reading stopped.

Figure — TCP flow control — sliding window, receive buffer


Step 3 — Free space is what the box shouts back: rwnd

WHAT. The empty part of the box — width minus occupancy — is the free room. The receiver announces this number to the sender. Its name is the receive window, written rwnd.

WHY announce it, and why this number. The sender is far away and cannot see the box. The only way the sender learns "how much can I safely send?" is if the receiver tells it. And the honest answer is precisely the free space: send more than that and the box overflows.

PICTURE. Same box as Step 2, now with the free region (cyan) labelled and an arrow leaving the receiver carrying that number back toward the sender.

Figure — TCP flow control — sliding window, receive buffer

Term by term: the whole box width, minus the amber band, leaves the cyan free strip. That cyan width is rwnd.


Step 4 — Now look at the SENDER's tape: two more markers

WHAT. Switch cameras to the sender. It also keeps two markers on the same byte-tape:

  • ==LastByteSent== — highest byte it has pushed into the network.
  • ==LastByteAcked== — highest byte the receiver has confirmed receiving (an ACK = acknowledgement).

WHY these two. Some bytes have left the sender but are still crossing the wire — the receiver has not confirmed them. Those are the risky bytes: they are heading for the box but are not counted in it yet. The gap between these two markers measures exactly that risk.

PICTURE. The sender's tape. Everything left of LastByteAcked is safely delivered (grey). The strip between LastByteAcked and LastByteSent is in-flight (amber) — sent, not yet confirmed.

Figure — TCP flow control — sliding window, receive buffer


Step 5 — Line the two tapes up: every in-flight byte is a future guest

WHAT. Put the sender's tape above the receiver's box and align them by byte number. Every amber "in-flight" byte on top is travelling toward the amber-or-cyan region below. Each such byte will demand a seat in the box when it lands.

WHY this alignment is the whole trick. The box's free space (cyan, rwnd) is the number of empty seats. The in-flight bytes are guests already on their way who have not sat down yet. If more guests are coming than there are empty seats, someone gets turned away — that is overflow — see Buffer overflow & packet loss.

PICTURE. The two tapes stacked and aligned. Cyan seats below, amber guests above, connected by falling arrows. As long as amber ≤ cyan, every guest finds a seat.

Figure — TCP flow control — sliding window, receive buffer

So the safe condition is literally "guests on the way ≤ empty seats":


Step 6 — The golden rule, and how much MORE you may send

WHAT. We just derived the flagship inequality. Rearranging it tells the sender its remaining budget right now.

WHY rearrange. The sender does not ask "am I already legal?" — it asks "how many new bytes may I launch this instant?" That is the empty seats minus the guests already dispatched.

PICTURE. The box with three labelled widths in a row: the amber in-flight guests, the still-usable slice (the send budget), and past that the wall.

Figure — TCP flow control — sliding window, receive buffer

Step 7 — Edge case: the window slides shut to zero, then how it re-opens

WHAT. Suppose the application stops reading entirely. LastByteRead freezes, LastByteRcvd keeps rising as data arrives, so the amber band grows until it fills the box. Then: The receiver advertises Window = 0; the sender must stop dead.

WHY this needs its own step. Zero is the degenerate value where the ordinary "an ACK slides the left edge right" motion cannot help — there are no ACKs to send once no data flows. The sender could wait forever for a re-opening that never comes. This is the zero-window deadlock.

The fix. The sender periodically launches a tiny 1-byte zero-window probe. The receiver's reply must carry its current window; once the app finally reads and frees space, that reply reveals a non-zero rwnd, and flow resumes.

PICTURE. Left half: box completely amber, rwnd = 0, sender halted. Right half: app has read a chunk (amber shrinks, cyan reappears), a probe arrow goes out and a fresh non-zero window comes back.

Figure — TCP flow control — sliding window, receive buffer

Step 8 — Edge case: the 16-bit ceiling and window scaling

WHAT. The Window field on the wire is only 16 bits wide, so the largest raw number it can hold is On a fast, long link this cyan strip is drawn far too short — the sender is forced to pause even though the real box is huge and the pipe could hold far more (this "pipe capacity" is the Bandwidth-delay product).

WHY a shift, not a wider field. Widening the field would break every old TCP that reads exactly 16 bits. Instead, both sides agree at the handshake on a shift count , and the real window is the field value multiplied by : Old TCPs that never saw the option simply ignore and keep the plain 16-bit meaning — nothing breaks.

PICTURE. Two boxes side by side: a tiny 65 535-byte box (unscaled) versus a large scaled box, with the multiplier labelled on the arrow between them.

Figure — TCP flow control — sliding window, receive buffer


The one-picture summary

Everything above collapses into a single aligned diagram: the sender's tape (grey acked · amber in-flight · white unsent) sitting exactly over the receiver's box (amber occupied · cyan free = rwnd), with the golden inequality written across the seam.

Figure — TCP flow control — sliding window, receive buffer
Recall Feynman retelling of the whole walkthrough

Picture a fixed-size box (that's RcvBuffer). Some data sits inside waiting to be picked up — the gap between "last byte that arrived" and "last byte read out" is how full it is (occupancy). The empty part left over is the free space, and the receiver shouts that number back as rwnd. Meanwhile the sender has bytes it launched but hasn't heard back about — the gap between "last sent" and "last acked" — those are guests already on the way (in-flight). Line the sender's tape over the receiver's box: every guest on the way needs an empty seat. So the one rule is guests on the way ≤ empty seats, i.e. LastByteSent − LastByteAcked ≤ rwnd, and the room you may still use is just seats minus guests. If the app stops reading, the box fills, seats hit zero, the sender halts — and tiny probe bytes keep asking "any room yet?" until a fresh shout re-opens it. And because the shouted number only fits in 16 bits, on fast fat links both sides agree at setup to multiply it by a power of two so the box can be advertised as big as it really is.


Connections

Concept Map

full

16-bit ceiling

RcvBuffer fixed width

occupancy = LastByteRcvd - LastByteRead

rwnd = free space

in-flight = LastByteSent - LastByteAcked

in-flight less-equal rwnd

rwnd = 0 stop

1-byte probe re-opens

scale by 2 power s