§3.5.5Flow Control

Transport layer Kurose & Ross pp. 246–248 · ~14 min read

  • flow control
  • receive window

Where you are

  • Application layer
  • Transport layer you are here
  • Network layer
  • Link layer
  • Physical layer

Flow control stops a fast sender from burying a slow reader, using one number the receiver puts in every segment — and it has one genuinely awkward corner.

Words you will meet

  • Flow control — matching the sender’s rate to the rate the receiving application reads.
  • RcvBuffer — the size of the receive buffer.
  • rwnd — the receive window: how much spare room is left in it.
  • Speed-matching service — the book’s name for what flow control is.

Why this matters

Everything so far has assumed the receiver keeps up. It often does not. The receiving application may be busy with something else entirely, and may not read its data until long after it arrives. A sender that keeps sending will then overflow the buffer and destroy data that arrived perfectly.

This is also the section where two ideas that look identical must be kept apart. Flow control and congestion control both throttle the sender, and the book warns in as many words that many authors use the terms interchangeably. The savvy reader distinguishes them, and sections 3.6 and 3.7 will be much harder if you do not.

A speed-matching service

Flow control eliminates the possibility of the sender overflowing the receiver’s buffer. The book’s phrase for it is exact — a speed-matching service. It matches the rate at which the sender is sending against the rate at which the receiving application is reading.

Not against the rate the network can carry. Not against the rate the receiver’s TCP (Transmission Control Protocol) can process. Against the rate the application reads.

Figure 3.38 — the receive buffer, and the number it produces
1 — Empty, and the whole buffer is offeredstep 1 of 5
the receive buffer at Host BRcvBufferfrom IP →→ the applicationspare roomrwnd = RcvBuffercarried in the receive-window field of every segment B sends

Before any data arrives, LastByteRcvd equals LastByteRead and the buffer is empty. Host B sets rwnd = RcvBuffer and advertises it.

Two pointers and a subtraction. Step through a slow reader filling the buffer up.

Read all steps as text
  1. 1 — Empty, and the whole buffer is offeredBefore any data arrives, LastByteRcvd equals LastByteRead and the buffer is empty. Host B sets rwnd = RcvBuffer and advertises it.
  2. 2 — Data arrives and the application keeps upBytes land in the buffer and the application reads them out at about the same rate. The gap between the two pointers stays small, so rwnd stays large and the sender is never held back.
  3. 3 — The application slows downThe receiving application is busy with something else. Data keeps arriving; nobody is reading it. LastByteRcvd runs ahead of LastByteRead and the gap widens.
  4. 4 — Full. rwnd = 0.The buffer is completely occupied by unread data. B advertises rwnd = 0, and Host A must stop sending. Nothing has gone wrong — the receiver is simply busy, and TCP has matched the sender to it.
  5. 5 — The sender keeps its side of the bargainHost A tracks LastByteSent and LastByteAcked. It keeps LastByteSent − LastByteAcked ≤ rwnd, so it can never put more unacknowledged data into the network than B has room for. That inequality is the whole of flow control.

Four variables, one subtraction

At the receiver, Host B:

  • LastByteRead — the last byte the application has read out of the buffer.
  • LastByteRcvd — the last byte that has arrived from the network and gone in.

TCP must never overflow the buffer, so LastByteRcvd − LastByteRead ≤ RcvBuffer. The receive window is whatever is left over:

rwnd=RcvBuffer[LastByteRcvdLastByteRead]rwnd = RcvBuffer - [\,LastByteRcvd - LastByteRead\,]

Because the spare room changes constantly, rwnd is dynamic. B puts its current value in the receive-window field of every segment it sends to A, and sets it to RcvBuffer at the start.

At the sender, Host A keeps LastByteSent and LastByteAcked. Their difference is the amount of unacknowledged data A has put into the network, and A’s whole obligation is:

LastByteSentLastByteAckedrwndLastByteSent - LastByteAcked \le rwnd

The receive window

What each symbol means

  • RcvBufferthe size of the receive buffer (bytes)
  • LastByteRcvdthe last byte that has arrived from the network (byte number)
  • LastByteReadthe last byte the application has read out (byte number)
  • rwndspare room, advertised in every segment (bytes)

Read aloud: Take the size of the buffer and subtract what is sitting in it unread. What is left is how much more the sender may send.

rwnd2,596 bytes

the buffer is holding LastByteRcvd − LastByteRead = 3,000 − 1,500 = 1,500 bytes
rwnd = RcvBuffer − [LastByteRcvd − LastByteRead]
     = 4,096 − 1,500
     = 2,596 bytes of spare room
the sender must keep LastByteSent − LastByteAcked ≤ 2,596
this fits in the 16-bit receive-window field

Change any number above and the arithmetic re-runs, carrying the units through.

Now drag LastByteRead down towards LastByteRcvd — a slow application — and watch rwnd reach zero. That is where the interesting problem starts.

A simplification the book makes here, worth noticing

This section assumes “the TCP receiver discards out-of-order segments”, so that the reader can see the forest for the trees.

That is worth flagging, because section 3.5.4 said the opposite about real implementations: the RFCs leave the choice open and practice is to buffer out-of-order segments.

Both statements are fine — this one is an explicit simplifying assumption, not a claim about reality. But it does mean the buffer arithmetic above is slightly tidier than the real thing, where the receiver may also be holding segments above a gap.

The awkward corner

Now the problem, and it is a real deadlock rather than a slowdown.

Suppose B’s receive buffer fills, so it advertises rwnd = 0. Host A stops sending, exactly as instructed. Suppose also that B has nothing of its own to send to A.

Then the application at B drains the buffer. Room reopens. And nobody tells A.

The zero-window deadlock, and the one byte that breaks it

Nothing is lost, nothing is corrupted, and neither host does anything wrong. The connection stops anyway.

message 3 of 3
Host Asending a large fileTimeHost Bslow applicationTimeblocked — waiting for a window updatebuffer empty — but no reason to send anythingstill blocked. Neither host will ever move.dataACK, rwnd = 0(the application drains the buffer — but B sends nothing)

Click any arrow to see what that message says and why it is sent.

Switch between the two runs. In the first, both hosts are behaving correctly and the connection stops forever.

Read this diagram as text
  1. Host A sends data to Host B.
  2. Host B sends ACK, rwnd = 0 to Host A. The buffer is full. B advertises a window of zero, which A must obey. B has no data of its own to send.
  3. Host B sends (the application drains the buffer — but B sends nothing) to Host A — lost. Here is the trap. TCP sends a segment only when it has data to send or an acknowledgement to send. B now has neither: A has stopped sending, so there is nothing to acknowledge. The window update that A is waiting for never leaves.

Lifelines, left to right: Host A (host), Host B (server).

In plain words

TCP sends a segment only when it has data to send or an acknowledgement to send.

With rwnd = 0, A has stopped sending. So B receives nothing, so B has nothing to acknowledge, so B sends nothing. A is never told the receive window reopened. The silence sustains itself, and neither host is doing anything wrong.

The specification’s fix puts the burden on the sender. A must continue to send segments with one data byte while B’s receive window is zero. Those segments are acknowledged, and eventually one of those acknowledgements carries a non-zero rwnd.

One byte, because it is the smallest thing that obliges a reply.

The ceiling nobody mentions until it hurts

Section 3.4.2 worked out that Lin’s path — 100 Mbps, 180 ms — needs about 2.25 megabytes in flight to keep the link busy.

Section 3.5.2 showed that the receive-window field is 16 bits.

What a window costs you on a long path

What each symbol means

  • windowthe most that may be outstanding at once (bytes)
  • RTTround-trip time (s)

Read aloud: A sender may have only one window of data unacknowledged at a time, so it can never send more than one window per round trip — whatever the link could carry.

Fastest this connection can go2.91 Mbps

at most one window may be outstanding per round trip
so throughput ≤ window / RTT = 65,535 bytes × 8 / 180 ms
                             = 2.91 Mbps
the link itself can carry 100 Mbps, so the window is using 2.91 % of it
to fill this path the window would need the bandwidth-delay product: 100 Mbps × 180 ms / 8 = 2,250,000 bytes
that is beyond the 16-bit field, so the window scale option must multiply it by 2^6

Change any number above and the arithmetic re-runs, carrying the units through.

The defaults are the largest window a 16-bit field can advertise, on Lin’s path. Compare the answer with the 100 Mbps the link actually offers.

65,535 bytes is about 45 segments, and about 2.9 Mbps

A sender may have at most one receive window of unacknowledged data outstanding, so it can never send more than one window per round trip. On Lin’s path that caps the connection at

65,535×80.1802.9 Mbps\frac{65{,}535 \times 8}{0.180} \approx 2.9\ \text{Mbps}

on a link that can carry a hundred. Under 3 % of it, and no amount of congestion-window growth in section 3.7.1 can get past it, because the sender obeys the smaller of the two windows.

This is what the window scale option in RFC (Request For Comments) 7323 is for. It multiplies the advertised value by a power of two agreed during the handshake. To reach 2.25 MB the scale factor must be 2⁶, giving a maximum of about 4.19 MB.

The book mentions “a window scaling factor for use in high-speed networks” in section 3.5.2 and never puts a number on it. This is the number.

Keeping the two apart

Flow control against congestion control
Flow controlthis sectionCongestion controlsections 3.6 and 3.7
Protects
From
The sender learns the limit
Variable
Where it is kept
Does UDP have it?

Cells marked ⓘ have a reason behind them — click to read it.

The book warns that many authors use these interchangeably. They are not interchangeable, and this grid is why.

And UDP (User Datagram Protocol) has none of this

UDP provides no flow control, so its segments may be lost at the receiver through buffer overflow.

A typical UDP implementation appends arriving segments to a finite buffer that sits in front of the socket. The process reads one entire segment at a time from that buffer. If it does not read fast enough, the buffer overflows and segments are dropped.

Silently. The sender is not told, and neither is the receiving application. From the outside it is indistinguishable from loss in the network — which, from section 3.3, UDP also does nothing about.

Check yourself

Check yourself

0 of 6 answered
  1. 1.Flow control and congestion control both slow the sender down. What is the difference?

    Ask who is being protected in each case.

  2. 2.predictSet the calculator to a 4,096-byte buffer with LastByteRcvd 3,000 and LastByteRead 1,500. What is rwnd?

  3. 3.Host B advertises rwnd = 0 and has no data of its own to send. Its application then drains the buffer. What goes wrong?

  4. 4.How does TCP break that deadlock?

  5. 5.predictSet the ceiling calculator to a 65,535-byte window on Lin's 100 Mbps, 180 ms path. What is the fastest the connection can go?

    Only one window may be outstanding per round trip.

  6. 6.What happens when a UDP receiver's buffer overflows?

What to remember

  • Flow control is a speed-matching service: the sender’s rate against the rate the application reads, not the network’s rate.
  • rwnd = 0 would deadlock, because a silent receiver has nothing to acknowledge and so never sends the update. The sender must keep probing with one-byte segments.
  • Flow control is not congestion control. One protects the receiver and is told the limit explicitly; the other protects the network and must infer it. UDP has neither, and overflows silently.