TCP 的窗口
TCP的窗口区分 接收窗口和拥塞窗口,接收窗口是由接收对端维护的,基于TCP头部中声明的接收窗口的大小和扩展选项中的扩大因子决定,而拥塞窗口是在发送端维护的一个虚拟窗口,为了避免在造成网络拥塞,造成发出去的包丢失或者完全无响应,主动限制发出去的包的数量。
实际的发送窗口的大小为min(rwnd, cwnd) rwnd即receive window, cwnd即 congestion window。
接收窗口
不同的接收窗口大小的设置会对传输性能产生影响。如果接收窗口设置得很小,则默认所有的发送都要在收到对端的ACK确认,且应用层处理完成,且通告窗口恢复后,才能继续后续的传输。这会导致带宽的大大浪费。如果接收窗口设置得太大,可能的情况是发送端逐渐扩大拥塞窗口,在网络如果真的出现拥塞的情况下,就会导致丢包,进而触发超时重传或者其他可能的重传机制,同样会对TCP的传输带宽产生负面影响。
窗口扩大因子
TCP头部的窗口大小为16位,但在网络硬件设施飞速发展的现在,该点已经成为了事实上的传输瓶颈。故而TCP新增了扩展选项,用于窗口扩大因子。
TCP头部通告窗口大小N,扩展选项通告扩大因子M,实际窗口为 N*(2^M)。最大可能为1GB,原来16位最大为65535,如果历史的windows和linux不启用该选项,则最大只有64KB。
基于iperf测试窗口对传输性能的影响
使用iperf3 的3.1.3版本测试,在wifi局域网环境下在centos和windows之间使用iperf进行带宽测试,以centos为server,windows为client。以类似如下命令行形式启动。
#server
# iperf3 -s
-----------------------------------------------------------
Server listening on 5201
#client
iperf3.exe -c 192.168.50.70 -i 1
可以通过wireshark看到默认使用了tcp扩展选项,默认的窗口大小是208KB。
传输的带宽大概如下,因为是无线连接,存在较大的波动。
针对上述窗口大小对性能影响的推论,我们尝试修改参数大小,来查看对传输速度的影响
iperf提供了client端的-w选项用于配置tcp的窗口大小,让我们使用-w选项指定窗口大小,来查看窗口大小是否对性能存在影响。以如下的方式启动客户端。
iperf3.exe -c 192.168.50.70 -i 1 -w 5K
可以看到速度确实大大降低了。
那是否确实iperf按我们的预期进行了设置呢?。可以再通过wireshark抓包进行确认,可以通过如下截图看到确实窗口大小声明为了5KB。
另外针对接收窗口在实际传输过程中的变化,比如上述序号24的包声明窗口为2420的情况,常见的可以理解的场景就是 接收方处理数据的速度跟不上内核接收数据的速度,导致内核接收缓存区就会被占满,导致当前的接收窗口小于声明的接收窗口。只有在应用层处理完数据后,接收窗口才会重新被释放出来,也就是我们通常所说的TCP滑动窗口。
在我的centos-7上。
默认的tcp数据接收窗口大小如下
net.core.rmem_default = 212992
cat /proc/sys/net/core/rmem_default
所以上述iperf3启动接收端时,实际上就是使用了这个默认的212992大小的208KB大小窗口。
最大的tcp数据接收窗口大小
net.core.rmem_max = 212992
cat /proc/sys/net/core/rmem_max
tcp窗口扩大因子使能查看。
cat /proc/sys/net/ipv4/tcp_window_scaling
对单个套接字的TCP接收窗口的设置,其实就是接收缓冲区的设置(以C为例)就是通过setsockopt函数设置SO_RCVBUF选项,设定接收端通告对方的接收窗口的大小。
那么如何设置最合适的tcp接收窗口大小呢?。《UNP》中的介绍是接收缓冲区的大小需要与TCP管道的容量相匹配。个人认为还需要加上不会触及应用的处理能力上限这一前提。
管道的容量称为带宽延迟积,通过将带宽(bit/s)和RTT相乘,再将位换算为字节计算得到,其中RTT显然可以通过ping简单得到。带宽是两个端点之间最慢链路的值,因为可能经过多层router,每两个router之间的带宽都不相同,只取决于全链路上协商带宽最低的两个router之间的带宽。如果带宽延迟积大于接收缓冲区,则管道其实不会处于满的状态,性能也就会低于期望值。带宽变大或者RTT变大时,套接字的缓冲区都需要有相应的增长。
而针对管道容量比较小,而接收窗口声明得太大,导致的拥塞问题。后续在拥塞窗口中再行说明。