第15章 TCP数据流与窗口管理;延时确认(Delayed Ack);Nagle算法;窗口通告;滑动窗口;窗口的滑动;零窗口和TCP持续计时器
"交互式"TCP连接是指该连接需要在客户端和服务器之间传输用户输入信息,如按键操作、短消息、操作杆、鼠标的动作等。
每个输入的字符会生成4个TCP报文段:客户输入字符的发送,服务端回复ack;服务端回显数据发送,客户端回复ack
服务端通常会将2,3段报文会合并
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
延时确认(Delayed Ack);Nagle算法;延时ACK与Nagle算法结合的潜在问题 =========================================================================================== 延时确认(Delayed Ack) 一般来说TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段可以实现该功能。累积确认可以允许TCP延迟一小段时间再发送ACK,从而可以将ACK和同方向上的其他需要传的数据结合发送。当然,TCP延迟的时间不能过长,否则对方会误认为数据丢失而出现不必要的重传。一般TCP实际时延最大取200ms。 采用延时ACK的方法可以减少ACK传输的数目(减少纯ACK包),可以一定程度的减轻网络负载。基于不同的操作系统,延迟发送ACK的最大时延可以动态配置。 Linux使用了动态调节算法,可以在快速ack(TCP_QUICKACK)和延时ACK之间切换。 ------------------------------------------------------------------------------------------ Nagle算法 Nagle 算法的基本思想是,当一个TCP连接中有在传数据(即已发送但还未确认的数据),小报文段(<SMSS)就不能发送,直到所有的在传数据都收到ACK。并且在收到ACK后,TCP需要收集这些小数据,将其整合到一个报文段中发送。Nagle 算法强制TCP遵循停等策略,即只有等待收到所有在传数据的ACK后才能继续发送。 Nagle 算法实现了自时钟控制:ACK返回越快,数据传输也越快。在相对高延迟的广域网中,减少了发送报文的数目。 但是在小数据包的情况下,例如上述的ssh应用,使用Nagle算法 没发送一个请求和响应包需要等待一个RTT,这就加长了整个传输过程时延。 Nagle算法的停等行为,在任一给定时刻,只有一个方向保持传输状态,即任一时刻只有一个包在传。 ------------------------------------------------------------------------------------------ 若将延时ACK与Nagle算法直接结合使用,很可能会影响传输性能。 短暂的死锁: 0.发送端往接收端发送了一个报文; 1.接收端在接收到报文后,因为延迟ACK策略,不会立即发送ack,而是处于等待状态,希望有数据一同捎带发送; 2.发送端由于开启了Nagle算法,必须等待接收到相应的ack; 3.此时发送端和接收端都处于等待状态 4.延迟ack计时器超时,等待状态才会被打破 解决方案:禁用nagle算法
流量控制与窗口管理
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
窗口通告;滑动窗口;窗口的滑动 ============================================================================================= 窗口通告 窗口通告信息的数值表示发送该窗口信息的通信方,在作为接收方时为即将到来的新数据预留的存储空间。 窗口通告信息实现的是对对端发送流量的限流 1.当TCP应用程序空闲时,就会排队处理这些数据,致使窗口大小字段保持不变。 2.当系统处理速度较慢,或者程序忙于执行其他操作,到来的数据返回ACK后,就需要排队等待被处理,这时新数据的可用存储空间就会减小,窗口的大小也即会减小。 3.若应用程序一直不处理这些排队等待的数据,TCP就必须采用策略限制以至于使发送端停止新数据的发送(若通告窗口为0,没有存储空间)。 每个TCP头部的窗口大小字段表明接收端可用缓存空间的大小,以字节为单位,长度为16位, 但在窗口缩放选项可用大于65535的值。 ------------------------------------------------------------------------------------------ 滑动窗口 作为全双工传输协议,每个TCP活动连接的两端都维护一个发送窗口结构和接收窗口结构。 TCP以字节为单位维护其窗口结构。 发送窗口结构:已发送已确认的数据(窗口外)、已发送但未经确认的数据(窗口内)、即将发送的数据(窗口内)、窗口右边界未移动前不能发送的数据(窗口外) 接收窗口结构:已接收并确认的数据(窗口外)、接收后将会保存的数据(窗口内)、不能接收的数据(窗口外) ------------------------------------------------------------------------------------------ 窗口的滑动 发送方的发送窗口滑动: 1.关闭(close),即窗口左边界右移:当已发送的数据得到ACK确认时,窗口左边界右移,窗口变小 2.打开(open),即窗口右边界右移:对端发来的可用窗口>发送方的窗口,窗口右边界右移 3.收缩(shrink),窗口右边界右移:【RFC1122】不支持这种做法! 零窗口:当左右边界相等时,此时发送端不能再发送新数据。这种情况下,TCP发送端会开始探测对方窗口。 接收方的接收窗口滑动(我的总结): 窗口左边界右移:数据接收并经过应用程序处理后,窗口左边界右移 不支持SACK的会话:只有到达数据序列号等于左边界时,数据才不会被丢弃 支持SACK的会话:序列号处于接收窗口范围内的数据都会被接收 窗口右边界右移:应用程序分配了新的缓存空间时,窗口右边界右移 不是只有应用程序进行数据处理,窗口右边界才会右移!也有可能设备缓存充足,系统可以不断地分配新的缓存。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
零窗口和TCP持续计时器 ============================================================================================= 零窗口: 当窗口值变为0时,可以有效阻止发送端继续发送,直到窗口大小恢复为非零值。 当接收端重新获得可用空间时,会给发送端传输一个窗口更新,通知其可以继续发送数据。 这样的窗口更新通知一般都为纯ACK包,不能保证其传输的可靠性。 ------------------------------------------------------------------------------------------ TCP持续计时器:发送端使用持续计时器间歇性的查询接收端,看其窗口是否已增长。持续计时器会触发窗口探测的传输,强制要求接收端返回包含窗口大小的ACK包。 【RFC1122】建议在一个RTO之后发送第一个窗口探测,随后以指数时间间隔发送 窗口探测包含一个字节的数据,采用TCP可靠传输,当TCP持续计时器超时,就会触发窗口探测的发送。与TCP重传计时器类似,该计时器也可以采用指数时间退避来计算持续计时器的超时。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
糊涂窗口综合征;大容量缓存与自动调优;紧急机制 ============================================================================================= 糊涂窗口综合征 基于窗口的流量控制机制,有可能会出现交换数据段大小不是全长的而是一些较小的数据段。 这种现象被称为糊涂窗口综合征(Silly Window Syndrome,SWS),由于每个报文段中有用数据相对于头部信息的比例较小,因此耗费的资源也更多,传输效率也越低。 SWS现象可能是由TCP连接的两端导致的: 接收端通告窗口较小,没有等到窗口变大才通告 发送端发送的数据段较小,没有等到将其他数据组合成一个更大的报文段。 要避免SWS,必须在发送端或接收端实现相关的规则: 对于接收端,不应通告小的窗口值。在窗口可增至一个全长的报文段(即接收端MSS)或者接收端缓存空间的一半(取两者中较小者)之前,不能通告比当前窗口更大的窗口值。 对于发送端,不应发送小的报文段,而且需由Nagle算法控制发送,更具体地,为避免SWS问题,只有至少满足以下条件之一时才能传输报文段: a)全长(MSS)的报文段可以发送 b)数据段长度≥接收端通告过的最大窗口值的一半 c)某一ACK不是目前期盼的(即没有未经确认的在传数据),或该连接禁用Nagle算法 发送端应用若在执行某些较小的写操作,条件c可以有效避免SWS ------------------------------------------------------------------------------------------ 大容量缓存与自动调优 在相似的环境下,TCP应用的缓存越小,吞吐性能越差,而且受TCP连接两端的影响,即时接收端指定一个足够大的缓存,发送端也可能指定一个很小的缓存,最终导致性能变差。 因此很多TCP协议栈中上层应用不能指定接收缓存的大小。在多数情况下,上层应用指定的缓存会被忽视,而由操作系统来指定一个较大的固定值或动态变化的计算值。 ------------------------------------------------------------------------------------------ 紧急机制 TCP头部有一个位字段URG用来指定“紧急数据”。应用要执行写操作时,可通过设置Berkeley套接字API(MSG_OOB)的特殊选项将数据标记为紧急。 当发送端TCP收到这类写操作要求时,会进入紧急模式的特殊状态。 它记录紧急数据的最后一个字节,用于设置紧急指针字段,随后发送端生成的每个TCP头部都包含该字段,直到应用停止紧急数据写操作,并且所有序列号大于紧急指针的数据都经接收端确认。 即使接收到通告0窗口,发送端也还是可以发送紧急数据! 【FRC6093】不再推荐设置紧急数据
=====================================================================================================
《网络排查案例课》10 | 窗口:TCP Window Full会影响传输效率吗?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
Wireshark提示"TCP Window Full"; =========================================================================== 一般来说,Wireshark 自己分析得到的信息,都会用方括号括起来,而 TCP 报文本身的字段,是不会带这种方括号的。 TCP Window Full : 接收端的接收窗口小于发送端的发送能力而出现的状况。 得出推论:瓶颈在接收端,TCP Window Full 也确实会影响传输速度。 Bytes_in_flight = latest_nextSeq - latest_ack_from_receiver 在途数据 = 本报文的nextSeq - 最新的ack 确认的Seq 当 在途数据 = 接收窗口大小(Calculated window size)时,wireshark将会提示"TCP Window Full" ***TCP Window Full 并不是总会出现的***,比如在接收窗口接近满但又不是完全满的时候,哪怕是离窗口满只差 1 个字节,Wireshark 也不会提示 TCP Window Full 了。 发送端并不会为了这几十KB去单独发送报文,因为这样不效率 但并不意味着发送端已经.... ------------------------------------------------------------------------ 速度 = 窗口 / 往返时间 (只有当接收端处理数据极快时,该公式才适用;若数据被放在缓存中,需要等待才会被处理,这时用这个公式就不准确了)(即 接收端有“数据滞留”的现象) 我们可以进一步优化为新的公式:速度 = 确认数据 / 往返时间。 这个改进后的公式,可以兼容这种有“数据滞留”现象的传输场景。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
案例:TCP Window Full 是导致异地拷贝速度低的原因吗? ===================================================================== 现象:在对该案例进行抓包分析时,发现几个报文提示了"TCP Window Full" TCP Window Full是接收端的接收窗口小于发送端的发送能力而出现的状况。 TCP Window Full是结果,而不是原因;当出现TCP Window Full时,传输速度确实会被影响