4.3.21 · D5Computer Networks

Question bank — TCP flow control — sliding window, receive buffer

1,785 words8 min readBack to topic

Before you start — the notation, defined here

Figure — TCP flow control — sliding window, receive buffer

The picture above is the whole vocabulary at once: the buffer is a bar, the read/received positions are edges, rwnd is the empty part on the right, and the window is what slides. Refer back to it whenever a reveal names a symbol.

Figure — TCP flow control — sliding window, receive buffer

The second picture shows occupancy over time as the app reads slowly and data pours in — watch rwnd (the free space, orange) squeeze down to zero, then reopen after a read.


True or false — justify

True or false: Flow control exists to protect the network from congestion.
False. Flow control protects the receiver's finite buffer (RcvBuffer) from a fast sender; protecting the network is the separate job of cwnd — see TCP congestion control — cwnd, AIMD.
True or false: rwnd is chosen and set by the sender.
False. rwnd is computed and advertised by the receiver (only it knows its own RcvBuffer and occupancy); the sender only obeys it. The sender's own limit is cwnd.
True or false: When rwnd = 0 the connection is broken and must be reset.
False. A zero window is a legal, temporary "pause" (the buffer is momentarily full); the sender halts data but keeps the connection alive with periodic 1-byte zero-window probes until a non-zero window is advertised.
True or false: A sender may have up to rwnd bytes in flight at any instant.
True. The rule is LastByteSent − LastByteAcked ≤ rwnd, so in-flight bytes can equal but never exceed the advertised window.
True or false: A sender may send up to rwnd more bytes right now.
False. It may send only rwnd − (LastByteSent − LastByteAcked) more, because bytes already in flight have reserved seats in the receiver's buffer.
True or false: If the application never calls read(), the receive buffer will eventually overflow and drop data.
False. As the buffer fills, occupancy grows, rwnd shrinks toward zero and the receiver advertises Window = 0, so the sender stops before overflow — that is precisely what flow control prevents.
True or false: The 16-bit Window field can advertise any window size the receiver wants.
False. Raw, it caps at bytes (16 bits); larger windows require the Window Scale option — a shift count negotiated at the handshake — which multiplies the field by .
True or false: Window scaling makes the Window field itself larger than 16 bits.
False. The field stays 16 bits; the scale option applies a bit-shift multiplier so old TCPs that ignore the option still read a valid 16-bit value (backward compatibility).
True or false: rwnd and cwnd are added together to find the sender's limit.
False. The sender's allowed in-flight bytes is min(rwnd, cwnd) — the tighter of the two constraints wins, since violating either causes harm (buffer overflow or network congestion).

Spot the error

Spot the error: "Occupancy = LastByteRead − LastByteRcvd."
The subtraction is backwards. Occupancy = LastByteRcvd − LastByteRead (bytes accepted minus bytes consumed); the received position is always at or ahead of the read position, so this order is never negative.
Spot the error: "The receiver sends a fresh window update the instant the app frees buffer space, so no deadlock is possible."
The receiver only emits a segment when it has data or an ACK to send. If no data is flowing after a zero-window, the update may never leave — that silent gap is the deadlock the zero-window probe fixes.
Spot the error: "Zero-window probes are sent by the receiver to announce it has room again."
Backwards. The sender sends the probes; the receiver's reply to a probe carries the current (possibly non-zero) rwnd, unsticking the flow.
Spot the error: "rwnd = RcvBuffer − LastByteRcvd."
It ignores what the app has already read. Correct: rwnd = RcvBuffer − (LastByteRcvd − LastByteRead), because consumed bytes free their space back into the buffer.
Spot the error: "Flow control shrinks the sender's window on packet loss."
That is congestion control (cwnd) reacting to loss/RTT. Flow control changes rwnd only because the receiver's buffer occupancy changed, independent of any loss.
Spot the error: "Since in-flight bytes will arrive, we can ignore them and just send up to the free space."
In-flight bytes already claim part of that free space. Ignoring them risks overflow, so they must be subtracted: budget = rwnd − (LastByteSent − LastByteAcked).

Why questions

Why is flow control described as receiver-driven rather than sender-driven?
Because only the receiver knows its own RcvBuffer size and how fast the app drains it; it advertises rwnd, and the sender merely respects that advertised number.
Why does subtracting in-flight bytes matter even though the buffer isn't full yet?
In-flight bytes are guaranteed future arrivals that have already reserved buffer space; counting them prevents the sender from over-committing room that is spoken for but not yet occupied.
Why does the left edge of the sliding window move when an ACK arrives?
An ACK raises LastByteAcked, so those bytes are no longer in flight — the window's left edge slides right, freeing budget and pulling the right edge forward so new bytes can be sent.
Why can't we just use a bigger Window field instead of the scale option?
A wider field would break every existing TCP that expects a 16-bit meaning; a negotiated shift multiplier keeps old implementations working since they simply ignore the unknown option.
Why is the zero-window probe just 1 byte rather than a full segment?
One byte is the minimum needed to force the receiver to respond with an updated rwnd; it avoids wasting bandwidth or pushing data the receiver may still have no room for.
Why can a high bandwidth–delay-product link starve even with a healthy connection?
If the un-scaled 65535-byte window is smaller than the Bandwidth-delay product (the bytes that fit in the pipe at once), the pipe empties before the next ACK returns, throttling throughput — window scaling exists to fix exactly this.

Edge cases

Edge case: The app drains the buffer faster than data arrives — what is rwnd?
Occupancy (LastByteRcvd − LastByteRead) stays tiny because reads keep pace, so rwnd stays near the full RcvBuffer; flow control never bites, and some other limit like cwnd governs instead. Intuitively, the cup empties as fast as it fills, so it always looks nearly empty.
Edge case: LastByteSent = LastByteAcked (nothing in flight) with rwnd = 0.
In-flight is zero, yet the free budget is rwnd − 0 = 0, so the sender still cannot send — the buffer is full even though the pipe is empty. It must wait for a probe reply carrying a non-zero rwnd. The two conditions (empty pipe, full cup) are independent.
Edge case: rwnd is large but cwnd is tiny (early slow start).
min(rwnd, cwnd) = cwnd limits the sender; the receiver has plenty of room but the network is the bottleneck. The tighter constraint always wins, and here that is the sender's cautious network estimate, not the buffer.
Edge case: Occupancy exactly equals RcvBuffer.
Then rwnd = RcvBuffer − RcvBuffer = 0, a legal fully-closed window; the receiver advertises Window = 0 and the sender pauses. No data is dropped because the sender was told to stop at the boundary, not after crossing it.
Edge case: A duplicate ACK arrives that doesn't advance LastByteAcked.
Since LastByteAcked is unchanged, in-flight (LastByteSent − LastByteAcked) is unchanged, so no new send budget is freed — the window's left edge only slides on fresh acknowledged progress, not on repeated old ACKs.

Recall One-line self-test

Cover the answers and run every section top to bottom. If you can justify each in a full sentence, you have internalised the reasoning, not just the formula.


Connections