KomuraSoft LLC
Chapter 4

Flow control — TCP's sliding window

With every ACK the receiver advertises 'how many more bytes I can accept,' and the sender keeps its unacknowledged bytes within that allowance. The sliding of this window sets the throughput ceiling.

A fast sender drowns a slow receiver

Even with retransmission in place, the promises are not yet complete. What if the sending server is powerful and the receiver is an underpowered mobile device? Arriving data first sits in the receiver's receive buffer, waiting for the application to read it with recv(). If the application cannot keep up with the sending pace, the buffer overflows, and data that made it all the way there gets discarded and must be retransmitted. That defeats the whole purpose.

So TCP adds the first half of promise 3: flow control. The mechanism rides on Chapter 3's ACKs. Every time the receiver returns an ACK, it also declares its free capacity — the window (rwnd): 'beyond the position I have confirmed, I can accept this much more.' The sender advances only so far that the bytes sent but not yet ACKed (in-flight) never exceed this allowance.

Diagram of the byte sequence divided into four ranges — sent and acknowledged, sent but unacknowledged, sendable within the window, and unsendable outside it — with the window sliding right as ACKs arrive

From the confirmed edge (ack = 20001), one window of 5000 bytes is the 'allowed to send' range. As ACKs advance, the whole window slides right — hence the name sliding window.

Painting the byte sequence in four colors

From the sender's viewpoint, the byte sequence can always be painted into four ranges. Take the example of ack = 20001, window 5000, and everything up to byte 23000 already sent.

Up to 20000: sent and acknowledged
Done with. Safe to drop from the buffer
20001–23000: sent, unacknowledged (in-flight = 3000 bytes)
In the air. Kept on hand as well, for retransmission if lost
23001–25000: unsent, sendable (remaining allowance 2000 bytes)
The window's right edge is 20000 + 5000 = 25000. Up to here may be sent without waiting for an ACK
25001 and beyond: unsent, not sendable
Outside the window. Wait for ACKs to advance and the window to slide

Sometimes the advertised window is 0 (a zero window). The receiving application is not reading its buffer, so the sender stops transmitting and waits, occasionally checking in with a small query called a window probe. If a capture shows a run of zero windows, the suspect is not the network but a processing stall in the receiving application.

Practice 4-1 — Hand-calculating the window edges

Learn to draw the line of 'how far may I send' precisely, in byte numbers.

Q1. What does the window value (rwnd) that the receiver advertises on its ACKs represent?

Q2. The sender received an ACK with ack = 20001 (confirmed up to 20000) and an advertised window of 5000 bytes. It has already sent up to byte 23000. How many additional bytes may it send now?

bytes

Q3. The receiver advertised a window of 0 (zero window). What is the sender's correct behavior?

The window size caps the speed — window ÷ RTT

Flow control has an important side effect. The sender keeps its outstanding data within the window, so at most one windowful can be sent per RTT. In other words, no matter how fat the bandwidth,

throughput ceiling = window ÷ RTT

cannot be exceeded. With a 100 KB window and an RTT of 100 milliseconds, 100,000 bytes ÷ 0.1 s = 1 MB/s is the ceiling. To use a line to the full, you need a window of at least bandwidth × RTT (the bandwidth-delay product, BDP). Much of "fast from the nearby server, slow only from the overseas one" is explained by this formula.

What the simulator does (text version): moving the three sliders — RTT, window, and bandwidth — updates the numbers for the amount sendable per RTT (= the window), the throughput ceiling given by window ÷ RTT, bandwidth × RTT (BDP), and the effective throughput (the smaller of the window-limited and bandwidth-limited values). A horizontal bar also shows how full the pipe of the path is with in-flight segments (fill ratio and an estimate of segments in flight), and a sentence states whether the current ceiling is set by the window or by the bandwidth.

Three experiments to try in the simulator

  1. Multiply only the RTT by 10 — window and bandwidth unchanged. Watch the ceiling fall to one-tenth and the pipe go nearly empty.
  2. Set the window equal to the BDP — the fill ratio hits exactly 100%. Find the boundary where the limiter flips from 'window' to 'bandwidth.' It should match the answer to the third question of Practice 4-2.
  3. Make the window larger than the BDP — the ceiling stops growing. Confirm that widening the window cannot push more through than the pipe itself.

Practice 4-2 — Window and RTT determine throughput

Verify the simulator's formula with your own hands. Let 1 MB = 1,000,000 bytes.

Q4. What is the throughput ceiling, in MB/s, of a connection with a 100 KB (100,000-byte) window and an RTT of 100 milliseconds? (1 MB = 1,000,000 bytes)

MB/s

Q5. On a fast 1 Gbps line, transfers can still be slow — but only to distant destinations (large RTT). Which is the correct reason?

Q6. To keep a path with 10 MB/s of bandwidth and an RTT of 100 milliseconds completely full, what is the minimum window in MB? (Compute bandwidth × RTT. 1 MB = 1,000,000 bytes)

MB

Key takeaways from this chapter

  • Flow control adapts to the receiver's circumstances. rwnd is the declared free space of the receive buffer
  • The right edge of what may be sent = confirmed edge + window. Keep in-flight bytes within the window
  • Throughput ceiling = window ÷ RTT. To use the line fully, window ≥ bandwidth × RTT (BDP)
  • The prime suspect for zero windows is the receiving application's read stall. Look at the app before blaming the network

But adapting to the peer alone is still not enough. The next chapter is the second half of promise 3 — congestion control, which adapts to the network's circumstances along the way. Another window, cwnd, enters the stage.