Transport

TCP Flow Control

Sliding window mechanism and flow control in TCP connections.

Introduction: Receiver Capacity and Sender Rate

TCP flow control addresses a rate mismatch between a sender that can produce data quickly and a receiver that consumes it more slowly. The receiver stores arriving bytes in its receive buffer and advertises the available capacity. The sender uses this information to bound unacknowledged data and avoid exceeding the receiver's available space.

TCP Flow Control addresses the mismatch between the rate of sending and processing data. A sending computer can generate and transmit data much faster than the receiving computer can process it. A server can transmit at gigabits per second, while a smartphone or older laptop performs other tasks and cannot keep up with reception.

Without flow control, the fast sender would fill the receiver's buffer. Data arriving after the buffer was full would have to be discarded, causing segment loss, retransmissions, and lower connection performance. TCP Flow Control lets the receiver tell the sender how much data it can accept at a given moment. The sender then limits unacknowledged data to the advertised receive window. This mechanism prevents buffer overrun. It is separate from congestion control, which limits sending in response to network conditions.

The Core Components of Flow Control

TCP flow control relies on two components working in tandem: a temporary storage area on the receiver side and a communication mechanism in the TCP header to manage that storage.

1. The TCP Receive Buffer

For each TCP connection, the receiver's operating system allocates a block of memory called the receive buffer. This buffer acts as a temporary holding area for incoming data. When TCP segments arrive, their data is placed into this buffer. The application running on the receiver (e.g., a web browser or email client) reads data out of this buffer at its own pace.

The receive buffer stores segment data until the application reads it. The network delivers segments, while the application consumes data at its own rate. Flow control limits the data placed in the buffer. The receiver communicates the available space to the sender.

2. The Receive Window (rwnd) Field

The communication of buffer space happens via the Window Size field (often called the Receive Window or 'rwnd') in the . This 16-bit field allows the receiver to advertise how much available space, in bytes, it currently has in its receive buffer.

An ACK can carry the receiver's currently advertised 'rwnd'. For instance, if the buffer has a capacity of 64 KiB and 10 KiB of data is waiting for the application to read, the receiver advertises an 'rwnd' of 54 KiB. The sender can then send at most 54 KiB of new data before receiving another update. The window value creates feedback that adapts the amount of data in transit to the receiver's available space.

The Sliding Window Mechanism Explained

Managing data transmission from the advertised receive window is called the sliding window mechanism. It keeps multiple unacknowledged bytes in transit, so the sender does not have to wait for an acknowledgment before sending every next segment.

The "window" is a conceptual range of sequence numbers that the sender is currently permitted to transmit. As data is sent and acknowledged, this window "slides" forward along the sequence number space of the entire data stream.

Window Terminology (Sender Side)

Let us define a few variables that the sender tracks for its send window:

  • Last Byte Sent (LBS): The sequence number of the last byte of data that has been transmitted.
  • Last Byte Acked (LBA): The sequence number of the last byte of data that the receiver has successfully acknowledged. All bytes before this are considered successfully delivered.
  • Advertised Window Size (rwnd): The most recent window size value received from the receiver.

From these, the sender calculates its effective send window, which is the amount of new data it can send:

Effective Window=rwnd−(LBS−LBA)\text{Effective Window} = \text{rwnd} - (\text{LBS} - \text{LBA})

This is the advertised window minus the amount of data already in flight (sent but not yet acknowledged). The right edge follows from the acknowledgment number and the advertised field value. The value is clamped at zero when the result would be negative. This is the receiver-side limit. In practice, the congestion window (cwndcwnd) also limits the sender, so new data cannot exceed the smaller of rwndrwnd and cwndcwnd.

Sliding-window process example

The following file-transfer scenario shows how the sliding window advances.

  1. Initial State:The connection is ESTABLISHED. The client (sender) wants to send a 10,000-byte file to the server (receiver). The server advertises an initial receive window ('rwnd') of 4,000 bytes. Both sides have synchronized their sequence numbers, starting at 1. The client has an empty send window; it can send up to 4,000 bytes.
  2. Sender Transmits Data:The sender's application provides the data. The sender creates several segments. For simplicity, let us say it creates two segments of 1,000 bytes each and sends them immediately.
    • Segment 1: Contains bytes 1-1000.
    • Segment 2: Contains bytes 1001-2000.
    The client's Last Byte Sent (LBS) is now 2000. It has 2,000 bytes of data in flight (sent but not yet acknowledged). Its usable window has shrunk to 4000−2000=20004000 - 2000 = 2000 bytes. It can still send 2000 more bytes before it must stop and wait for an ACK.
  3. Receiver Acknowledges Data: The server receives both segments correctly. Its application reads the first 1000 bytes from its buffer, freeing up space. The server sends back an ACK segment.
    • Acknowledgment Number: 20012001 (confirming receipt of all bytes up to 2000 and requesting the next one).
    • Window Size: It still has a total buffer of 4,000 bytes. It has received bytes 1001-2000 which are still in the buffer, but the application read the first 1000. Let's say it has 3,000 bytes of free space now. It advertises 'rwnd' = 3000.
  4. Sender Slides the Window:The client receives the ACK. It now knows bytes 1-2000 have been successfully delivered. It can "slide" its window forward.
    • Its Last Byte Acked (LBA) is now 2000.
    • The amount of data in flight becomes 0 (LBS−LBA=2000−2000=0LBS - LBA = 2000 - 2000 = 0).
    • It also updates its understanding of the receiver's window to the newly advertised 'rwnd' of 3000.
    • Its usable window is now a full 3,000 bytes. It can immediately send bytes 2001 through 5000.

This process of sending data, updating the amount in flight, receiving ACKs, and sliding the window forward repeats continuously. The rwnd\text{rwnd}value changes the receiver-side limit, while an ACK with a higher acknowledgment number moves the window's left edge and releases space for subsequent bytes.

Special Cases and Problems in Flow Control

The sliding window mechanism handles ordinary transfers, but zero windows and small updates require separate TCP procedures.

The Zero-Window Problem and Probes

What happens if the receiver's application stops reading data, and its receive buffer fills up completely? In this case, the receiver will advertise a window size of 0. This is a clear signal to the sender: STOP sending new data immediately. The sender pauses new data transmission, while TCP still permits zero-window probing.

This creates a potential deadlock. The sender is now waiting for an update with a non-zero window. The receiver will only send such an update after its application reads data, but the segment containing that window update could get lost in the network. If it is lost, the sender would wait forever, and the connection would stall.

To prevent this, after receiving a zero-window advertisement, the sender starts a persist timer. When the timer expires, the sender usually sends a one-byte Zero-Window Probe if new data is available, or retransmits an earlier segment. The probe prompts an acknowledgment containing the current window size. If it remains zero, the sender resets the timer and tries again later. If it is non-zero, the sender can resume transmission. Probing limits the risk that a lost window update permanently stalls the connection.

Silly Window Syndrome (SWS)

SWS causes many very small data segments and can substantially reduce TCP performance. It can result from frequent small window advertisements by the receiver or small writes by the sender. Limiting updates and combining data reduces the overhead.

  • Receiver with small updates:If the receiver's application reads data one byte at a time, one byte of buffer space becomes free after each read. The receiver might advertise a window of only 1 byte. The sender would send a single-byte segment with a 20-byte TCP header and a 20-byte IP header. Transmitting 1 byte of data in a 40-byte segment is inefficient.
    Solution: The receiver should wait until a significant amount of buffer space is free (e.g., half the buffer, or at least one MSS) before advertising a larger window.
  • Sender sending single bytes:The sender's application can generate data one byte at a time, for example from keystrokes in a remote terminal session. Sending each byte as a separate TCP segment creates 40 bytes of header overhead for 1 byte of data.
    Solution: . It allows the sender to send the first small segment, but then it holds any subsequent small pieces of data in its buffer. It waits for the acknowledgment of the first segment to return before bundling all the buffered small pieces into a single, larger segment and sending it.

Adapting to Modern Networks: The Window Scale Option

The original TCP design used a 16-bit Window Size field, limiting the maximum receive window to 65,535 bytes (about 64 KiB). That value suited the network parameters of the time, but it limits throughput on fast connections with substantial delay.

The limiting quantity is the Bandwidth-Delay Product (BDP). BDP is the amount of data that can be in transit on a path during one RTT.

BDP=Bandwidth×Round-Trip Time\text{BDP} = \text{Bandwidth} \times \text{Round-Trip Time}

BDP is the amount of data that can be in transit during one RTT. To use the available bandwidth, the send window should be at least as large as the BDP.

For a connection from New York to London with 1 Gbps bandwidth and a 100 ms RTT, the BDP is about 12.5 MB. This follows from 1 000 000 000×0.1/81\,000\,000\,000 \times 0.1 / 8. A 64 KiB window is much smaller than that value. The sender would send 64 KiB of data, then wait for it to cross the Atlantic and for the acknowledgment to return before sending more.

To solve this, the TCP Window Scale option was introduced. During the three-way handshake, both sides can negotiate a scale factor from 0 to 14. The option appears only in SYN segments, and the negotiated shift is applied when interpreting the 16-bit window field in later segments. Each side negotiates its own factor, which applies to data flowing from the other side. The factor is a left-shift count applied to the 16-bit Window Size field. For example, a scale factor of 8 multiplies the field value by 282^8 (256). The scale factor does not widen the header field; it changes how its value is interpreted. The sender still observes both the advertised receive window and its own congestion window. The option allows an effective receive window of about 1 GiB, enabling TCP to use the capacity of fast networks.

Related articles