TCP的滑动窗口(Sliding Window)

-- TOC --

TCP滑动窗口的主要作用是实现流量控制,这个流量具体特指某一对发送方和接收方之间的流量,接收方根据自己的处理能力,动态调整自己的可接收数据窗口大小,通过ACK报文将窗口大小告知发送方,发送方根据收到的窗口大小,调整发送数据的流量。

从报文结构上看,窗口最大65536。

有了滑动窗口,发送方就算有海量等待发送的数据,也只能按照接收方给出的窗口大小来慢慢发。

滑动窗口带来的另一个好处是,只要窗口大小允许(并且满足发送条件),发送方不必等待前面数据的ACK,也可以发送数据。接收方可以延时确认,用后面数据包的ACK,一并把前面的数据都确认了,减少了报文数量。

Acknowledgement of Delay

通常,TCP收到数据后不会立刻发送ACK确认,它会延迟发送,还可以和对方需要的数据一起发送(数据捎带ACK),或者是等待第二个数据包来了后,直接回复第二个ACK,通常的实现采用的延迟是200ms。

发送方要在收到了ACK之后,才会将数据(已发送并确认)从发送缓存中移除。

接收方有可能会关闭窗口,即设置窗口大小为0,此时发送方停止发送数据,直到窗口重新打开。为了防止重新打开窗口的报文丢失造成死锁,发送方在窗口关闭后开启,启动一个定时器,定时发送窗口探测报文。

为了公益,似乎大家都不喜欢发送小包,IP头加上TCP头至少有40bytes,发送小包似乎非常不划算,overhead太高了。因此,当接收方通告很小的窗口时,发送方还义无反顾的发送小数据包,这种现象被称为糊涂窗口综合征(Silly Window Syndrome)。防止这种糊涂的情况,可以让接收方不要发送小窗口,发送方默认有Nagle算法,可以实现拒绝连续发送小包。

糊涂窗口综合症 SWS (Silly Window Syndrome)

接收方防止发小窗口的策略如下:

当「窗口大小」小于 min(MSS,缓存空间/2),就向发送方通告窗口为0,这阻止了发送方再发数据。等到接收方处理了一些数据后,前述条件不再满足时,就可以把窗口打开让发送方发送数据过来。

Window Scaling

In the TCP header, the window size field is of size 16 bits. Using this field TCP users (client or server) can advertise a maximum of 2^16 = 65536 bytes or 64 KiB as its buffer size to its other end-user. But modern endpoint devices (e.g., phones, tabs, PCs, etc) have much more memory available in their buffers. This 16-bit field was enough during the initial design of the TCP in the 1980s. But today memory is cheaper and the network is very fast hence situation is different. Therefore, if the endpoint has more than 64 KiB memory available in its buffer for a TCP connection, how does it convey this information to the peer endpoint using that same 16-bit window size field?

简单说,最大64KiB的Window Size,还是太小了,要想办法扩...

There are two possible solutions:

  1. Increase the window size field from 16 bits to 30 bits. But this is practically not possible. It is impossible to update this field in all the devices which are using the TCP across the globe. (改协议字段,几乎不可能)
  2. Change the definition of window size. Use the scale factor to convey 30 bits window size using only 16 bits field in the TCP header. This is called window scaling. (修改协议字段的定义,这就是window scaling)

It solves the scalability issue with receiver window or advertised window or user buffer without increasing the size of the Window field (which is 16 bits) in the TCP header. It expands the definition of the Window field to 32 bits and then uses a scale factor to carry this 32-bit value in the 16-bit window field of the TCP header. The scale factor is carried in a new TCP option called Window Scale.

There is an options field in the TCP header which was designed to accommodate all the future optimizations in the TCP. Window scaling also makes use of that option. The client first shares its scaling factor with the server in the SYN packet. When the server receives the scaling factor of the client in the SYN packet; then it sends its scaling factor using the same option field in the SYN+ACK packet which is an acknowledgment packet for the connection request made by the client. There is the strict constraint that a Window Scale option received on any other packet (except the SYN packet sent by the client first) should be ignored by the server (server should not share its scaling factor first).

client在SYN中通告自己的scaling factor,server在SYNACK中回应,server不能首先通知自己的scaling factor。option中的通告是Window左移的bit数,这个机制最大可以让window size到1GiB。

Linux内核默认开启window sacling:

$ sudo sysctl -a | grep window_scaling
net.ipv4.tcp_window_scaling = 1

本文链接:https://cs.pynote.net/net/tcp/202303241/

-- EOF --

-- MORE --