
Flow control: the receiver's room
Every TCP acknowledgement carries a second number, the window, which is how many more bytes the receiver has room for. It protects the receiver's buffer and says nothing about the network. A window of zero stops the sender until the receiver opens it again.
Next to the acknowledgement number, each TCP segment carries a window. It counts, from the acknowledged byte on, how many more bytes the receiver will accept. The sender must not have more than that outstanding. If the window is 500 and the acknowledgement is 800, the sender may send bytes 800 up to but not including 1300, until a newer acknowledgement says otherwise.
Room in the buffer
The window is the free part of the receiver's buffer. A byte that has arrived in order and not yet been read by the program takes room, and a byte the program reads gives it back. So the window moves with the reader, and not with the network: a program that reads slowly, or has stopped reading, makes the window shrink on every acknowledgement, and the sender slows to the pace of the reader without any error.
A window of zero
When the buffer is full, the receiver advertises zero. The sender stops, and it is not waiting for a lost acknowledgement. It has been told there is no room. It does not simply wait for news, because the update that reopens the window might itself be lost. So it sends small probes now and then, and the receiver answers each one with its current window. In the bench the probe is an empty segment, and in real stacks it is a very small segment. When the program reads and room opens, the receiver sends the new window, and the sender goes on.
How big a window can be
The window field is 16 bits, so without the window scale option of RFC 7323 it cannot say more than 65,535 bytes. With the option a window can reach a gibibyte, 2^30 bytes. The option is agreed once, in the handshake.
An ESP32 keeps its buffers small on purpose, to save RAM. In ESP-IDF the default TCP window is 5760 bytes, which is four full segments of 1440, and window scaling is off by default, so a larger buffer could not be advertised past 65,535 bytes anyway. Since a sender cannot have more than a window in flight, the rate it can reach is at most the window divided by the round trip. With 5760 bytes and a round trip of 50 ms that is about 115 kilobytes a second, however fast the link is. That is arithmetic, not a measurement. The Arduino core sets its own sizes, and this page gives no number for its window.
What it does not do
The window says how much the receiver can hold. It says nothing about the desks in between. A sender with a wide open window can still send more than a router can pass, and the next lesson is about what happens then.
Common mistakes
- Not reading a socket and wondering why the other side stalls. Unread bytes fill the buffer, the window goes to zero, and the sender stops with no error on either side.
- Taking a bigger buffer for a better link. A larger window allows more in flight, and it does nothing about a path that is already full.
- Reading a window of zero as a failed connection. The sender is waiting for room, and it asks again by itself.
Edit this page — content/fundamentals/tcp-udp/receiver-window.mdx
Discuss this article
Ask about this page. The answer stays here, on the page it belongs to, for whoever hits the same wall next.