目录
15.1 引言
15.2 交互式通信
15.3 延时确认
15.4 Nagle 算法
15.4.1 延时ACK与Nagle算法结合
15.4.2 禁用Nagle算法
15.5 流量控制与窗口管理
15.5.1 滑动窗口
15.5.2 零窗口与TCP持续计时器
15.5.3 糊涂窗口综合征
15.5.4 大容量缓存与自动调优
15.6 紧急机制
15.7与窗口管理相关的攻击
15.8总结
15.1 引言
两类TCP传输:
交互式传输:
单个报文小,如ssh,网络游戏中账号信息,鼠标操作等信息。
批量传输:
即大量数据传输。如文件共享下载,web访问。
需流量控制,防止接收端溢出。
15.2 交互式通信
网络中大部分是大批量传输,少部分是交互式传输。
ssh是典型交互式数据。
特点:小包。
ssh每个输入一个字符会生成4个TCP数据段:
1. 客户端指令输入
2. 服务器ACK
3. 服务器执行结果
4. 客户端ACK
其中2,3可合并一起发送,即ACK报文携带回显数据,这叫延迟ACK。
TCP段PSH标志含义:
表示发送端没有其他数据需要传输。而接收端收到数据后应立即传递给应用层。
因为SSH客户端产生的都是短小信息,通常SSH TCP报文都带PSH标志。
15.3 延时确认
延迟ACK:
TCP不会对收到的每个数据都回复一个ACK,而是通过累积ACK后将延迟的ACK和后续需要传的数据结合发送。
常用于批量数据传输中。
好处:减少ACK数目,减轻网络负载。
而快速ACK,即每个报文都会回复一个ACK。
15.4 Nagle 算法
SSH客户端一个单击动作会产生四个TCP报文,其中包含TCP头,IP头等开销,代价很高,会加重广域网阻塞。
解决方法:Nagle算法
Nagle翻译:突然
Nagle原理:
若发送端没有收到所有数据的ACK时,不发送小报文。直到所有在传数据收到ACK,在这个等待期间会整合多个小数据,通过一个更大报文发送。
所以ACK返回越快,数据发送越快。
优点:在高延迟网络中可整合小数据,减少小包数目。
缺点:增加了时延。
Nagle算法适用场景:
频繁传输小数据块,并延迟不敏感。
15.4.1 延时ACK与Nagle算法结合
延时ACK:延迟发送ACK,用于减少ACK包的数量,降低网络开销。
Nagle算法:等待收到所有已发送数据的ACK后,期间合并小包成大包再发送数据。
所以可知,两者结合使用导致短暂死锁。
所以如果SSH服务器开启了延迟ACK功能,客户端最好禁用Nagle。
15.4.2 禁用Nagle算法
Nagle算法增加了时延,对实时应用不适合。
如SSH,实时网络游戏。
内核协议栈禁用Nagle:
net.ipv4.tcp_no_delay = 1
应用程序禁用:
设置socket TCP_NODELAY选项
setsockopt(sockfd, IPPROTO_TCP, TCP_NODELAY, (void *)&flag, sizeof(int))
15.5 流量控制与窗口管理
每个TCP报文段包含:序列号,ACK号,窗口大小。
ACK号:下一次期待收到的报文序列号,说明之前的数据已全部接收。
窗口大小:向对方指示,自己的接收缓冲区还剩多少空间。
15.5.1 滑动窗口
每对TCP连接的两端都维护一个发送窗口和接收窗口。
其中发送窗口大小由对端的接收窗口通告。
在没收到数据的ACK情况下,最多可发送的数据量就是发送窗口大小。
滑动窗口作用:
优化网络利用率:
滑动窗口允许发送方连续发送多个数据段而不必等待每个数据段ACK。提高网络利用率。
避免拥塞:
发送方根据接收方通告的窗口大小来控制发送量,避免发送过快而引起拥塞。
发送窗口:
一旦收到已发送数据的ACK后,就移动窗口,可继续发送更多数据。
接收窗口:
把接收窗口值给通告给发送方。
收到数据的序列号小于左边界RCV.NXT,就是重复报文,丢弃数据。
收到数据的序列号大于右左边界RCV.NXT+RCV.WND,超出处理范围,丢弃数据。
15.5.2 零窗口与TCP持续计时器
TCP通过接收端的窗口通告实现流量控制。指示接收端缓冲区可接收数量。
通告的接收窗口为0时,可阻止对方发送。
接收端缓冲区重新有可用空间时,可传输一个窗口更新给发送端。该窗口更新是一个纯ACK,不包含数据。
问题:
如果接收端的窗口更新报文丢失,而发送方需要等到窗口更新报文才可继续发送。造成死锁。
解决方法:
周期向接收端查询窗口值,即发送窗口探测,接收端回复一个ACK报文,其中包含窗口大小。
TCP报文是纯ACK,则不会重传ACK。
TCP报文不是纯ACK,会重传ACK和数据。
15.5.3 糊涂窗口综合征
糊涂窗口综合征SWS
出现原因有:
接收端没等到窗口变大就通告,导致通告的窗口小。
发送端没等到将小数据合成更大报文就方法,导致发送数据小。
坏处:
传输的TCP数据很小。传输效率低。
解决方法:
接收端不通告小窗口值。
发送端不发送小报文段,由Nagle控制如何发送。
15.5.4 大容量缓存与自动调优
不必提前设置一个过大发送/接收缓存。而是根据待传数据大小,不断估算缓存大小,不断通告窗口大小。
即窗口自动调优。
如何设置窗口的自动调优范围:
接收窗口:
net.ipv4.tcp_rmem= 4096 87380 174760
发送窗口:
net.ipv4.tcp_wmem= 4096 87380 174760
上述三个值分别是最小值,默认值,最大值。
最小值:
应用程序设置发送缓冲区小于最小值时,内核自动扩大到最小值,如4096。
默认值:
应用程序未指定发送缓冲区大小时,内核会使用这个默认值。
最大值:
应用程序不能设置发送缓冲区的大小超过最大值。
自动调优:
当接收端根据接收缓存区剩余大小,动态发送窗口更新报文给发送端,控制发送端发送速率。
若TCP的接收缓存区太小,会严重限制TCP吞吐量。
小结:接收窗口和发送窗口值会根据网络环境和系统配置而动态调整,通常范围为几KB到几MB。
15.6 紧急机制
TCP URG标志:
指示该TCP报文包含紧急数据。
MSG_OOB标志可发送接收带外数据。
带外数据:使用带外(额外)通道来传输数据,可不受滑动窗口控制,实现传输紧急数据。
MSG_OOB使用方法如:
send(socket_fd, buff, buff_len, MSG_OOB);
recv(socket_fd, buffer, MAX_BUFFER_SIZE, MSG_OOB);
MSG_OOB数据比普通数据有更高优先级。
15.7与窗口管理相关的攻击
通告非常小的窗口,让发送方缓慢发送,并保持忙碌发送,耗尽资源。
15.8总结
延迟ACK:
使用场景:交互式通信中,当接收方收到数据后,延迟回复ACK,等到有数据发送时再一起携带ACK发送。
优点:减少包数量。
缺点:延时增大。
Nagle算法:
使用场景:广域网中RTT较大的环境中,采用Nagle算法,可以合并多个小数据包成一个,再发送。
优点:减少包数量,降低传输开销。
缺点:延时增大。
延迟ACK和Nagle算法会短暂死锁,有的会禁用Nagle算法。
通常延时不敏感的交互式应用可使用Nagle。
当接收端的接收缓存为空,会通告窗口为0。此时发送端会停止发送,并周期发送窗口探测,直到通告不为0。
糊涂窗口综合症:
接收端通告了小窗口,发送端立即发送小数据,导致网络太多小数据。
解决方法:
接收端不通告小窗口。
发送端不发送小数据。