TCP Send Window, Receive Window, and How it Works

Carson
8 min readJun 1, 2021

TCP sliding window includes the send window and the receive window. Let’s start with the relatively complex one — the send window.

Send Window

The diagram above is a snapshot from the sender’s point of view. We can categorize the data into 4 groups:

  1. Bytes that sent and acknowledged (blue)
  2. Bytes that sent but not yet acknowledged (yellow)
  3. Bytes that not sent and the recipient ready to receive (green)
  4. Bytes that not sent and the recipient NOT prepared to receive (grey)

Category 3 is also referred to as the usable window because this is the window the sender can use.

The send window consists of yellow and green parts. These bytes are either already sent or can be sent.

The usable window could be empty when the sender sends 21–25 bytes and uses all bytes in the usable window. The send window remains the same.

When the sender receives ACK for 16–19 bytes, the send window slides 4 bytes to the right. An updated usable window is available for the following bytes in the queue.

Some definitions could help us further understanding complex cases later in this post.

  • SND.WND, standing for Send Window
  • SND.UNA, standing for the Send Unacknowledged pointer, pointing to the first byte of the send window
  • SND.NXT, standing for the Send Next pointer, pointing to the first byte of the usable window

Based on these definitions, we can represent the usable window size in a formula.

Receive Window

The receive window has 3 categories:

  1. Bytes that received and acknowledged
  2. Bytes that not yet received but the transmitter permitted to send
  3. Bytes that not yet received and the transmitter may NOT allow to send

Category 2 is known as Receive Window, also be referred to as RCV.WND.

Similar to the send window, a pointer RCV.NXT, standing for the Receive Next pointer, pointing to the first byte of the receive window.

The receive window is not static. If a server runs efficiently, the receive window could expand. Otherwise, it could shrink.

The receiver communicates its receive window by indicating the size in the Window field in the TCP segment header. When the sender receives it, this window size becomes the usable window.

Sending and receiving segments take time. Therefore, the receive window is not equal to the usable window at a specific moment.

A Simplified Example

Let’s simulate a request and replies to better understand how the sliding window works.

Two modifications simplify our calculation.

  1. We ignore Maximum Segment Size (MSS). MSS varies depending on the selected network route.
  2. We make the receive window equals to the usable window, and both remain the same during the process.

Above is a diagram showing the 10-step example.

The client requests a resource, and the server responses to it in three segments:

  1. A 50-byte header
  2. An 80-byte body part 1
  3. A 100-byte body part 2

Each side can be a sender and a receiver at the same time.

We assume the client’s Send Window (SND.WND) is 300 bytes and Receive Window (RCV.WND) is 150 bytes. Therefore, the server’s SND.WND is 150 bytes and RCV.WND is 300 bytes.

This is the starting state of the client.

We assume it has previously received 300 bytes from the server, so RCV.NXT points to 301.

Since it has not yet sent anything, both SND.UNA and SND.NXT point to 1.

Based on the formula, the client’s usable window is 1 + 300 - 1 = 300.

This is the starting state on the server, mirroring the state on the other side.

Because it has sent 300 bytes, both SND.UNA and SND.NXT point to 301.

RCV.NXT points to 1 because the client has not yet sent any requests.

The server’s usable window is 301 + 150 - 301 = 150.

Now, step 1 begins.

The client sends its first 100-byte request. At this moment, the windows change.

  • These 100 bytes are sent and not ACK yet. Therefore, SND.NXT slides 100 bytes to the right.
  • Other pointers stay.

The usable window changes to 1 + 300 - 101 = 200.

At step 2, our focus moves to the server.

  • When the server receives the request, RCV.NXT slides 100 bytes to the right.
  • Then it sends a 50-byte reply with ACK. These 50 bytes are sent and not ACK yet, so SND.NXT moves 50 bytes to the right.
  • SND.UNA stays.

The usable window changes to 301 + 150 - 351 = 100.

Move to the client.

  • When it receives the 50-byte reply, RCV.NXT moves 50 bytes to the right.
  • SND.UNA slides to the right when it receives the ACK for the previous sent 100 bytes.
  • SND.NXT stays as the client does not send any data.

The usable window changes to 101 + 300 - 101 = 300.

Again, move to the server’s end.

The usable window is 100 bytes. It is OK for the server to send the 80-byte segment.

  • SND.NXT slides 80 bytes to the right.
  • SND.UNA stays because the last 50-byte has not yet been acknowledged.
  • RCV.NXT stays since the server does receive any data.

The usable window changes to 301 + 150 - 431 = 20.

The client receives the first part of the file and immediately sends the ACK.

  • When the client receives the 80-byte data, RCV.NXT moves to the right.
  • Other points stay.

The usable window stays at 300.

At this moment, the server receives the ACK for step 2 when it sends a 50-byte reply.

  • SND.UNA moves 50 bytes to the right.
  • Other pointers stay.

The usable window changes to 351 + 150 - 431 = 70.

The server, again, receives another ACK for step 4 when it sends the first 80-byte part of the file.

  • SND.UNA moves 80 bytes to the right.
  • Other pointers stay.

The usable window changes to 431 + 150 - 431 = 150.

At step 8, the server sends the 2nd part of the file — 100 bytes.

  • SND.NXT moves 100 bytes to the right.
  • Other pointers remain the same.

The usable window changes to 431 + 150 - 531 = 50.

It’s the client’s turn.

  • RCV.NXT moves 100 bytes to the right when the client receives the 100-byte.
  • Other pointers stay.

The usable window remains unchanged.

Finally, the server receives the ACK for the previous response.

  • SND.UNA moves 100 bytes to the right.
  • Other pointers stay.

The usable window changes to 531 + 150 - 531 = 150.

When Window Changes

Previously, we assume the send window and the receive window remain unchanged. This assumption is not correct in an actual example.

Bytes in both windows exist in the operating system buffer, which could be adjusted. For example, the available space in the buffer shrinks when our application does not read bytes from it fast enough.

Let’s introduce the window change in this case and see how it affects the usable window.

We simplify this case to focus the usable window on the client. In this example, the client is always the sender, and the server is the receiver.

When the server sends an ACK, it includes the updated window size in it as well.

A the beginning, the client sends the first 150-byte request.

  • These 150 bytes are sent and not ACK yet.
  • The usable window shrinks to 150 bytes.
  • The send window remains at 300 bytes.

When the server receives the request, the application reads the first 50-byte, and 100 bytes are still in the buffer, taking a 100-byte available space from the receive window. Therefore, the receive window shrinks to 200 bytes.

Next, the server sends the ACK with an updated 200-byte receive window.

The client receives the ACK and updates its send window size to 200.

At this moment, the usable window is the same as the send window because all 150 bytes are acknowledged.

Again, the client sends another 200-byte request, using all available space in the usable window.

After the server receives the 200 bytes, the application is still running slow, reading merely 70 bytes in total and leaving 280 bytes in the buffer.

This leads to another shrink on the receive window. Now, we have only 20 bytes left.

In the ACK message, the server shares the updated window size with the client.

Again, the client updates its send window to 20 bytes after receiving the ACK. The usable window becomes 20 bytes as well.

In this case, the client stops sending any request larger than 20 bytes until it receives another window update in the following message.

Will we be stuck at the 20-byte usable window if no more messages coming from the server?

We won’t. To avoid this case, the client’s TCP periodically detects the window size.

Once more space is released, the usable window expands, and more data can be sent.

Takeaways

  • The usable window’s calculation is the key to understand the TCP sliding window.
  • To learn the usable window’s calculation, we need to understand 3 pointers — SND.UNA, SND.NXT, and RCV.NXT.
  • Assuming a never-change window size could help us understand the progress.

--

--