TCP数据流与窗口管理

Posted vector6_

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了TCP数据流与窗口管理相关的知识,希望对你有一定的参考价值。

TCP数据流与窗口管理

TCP是基于流的数据传输协议,本文主要对TCP的动态数据传输进行分析,即流量控制及窗口管理。TCP通过动态调节窗口大小来控制发送端的操作,确保接收端不会溢出。当然,流量控制的思想也可扩展应用于其他问题,可以保护接收端免于溢出,还可以处理中间传输网络的拥塞问题。

交互式通信

我们知道交互式TCP连接需要在客户端和服务器之间传输用户输入信息,交互式数据一般会比较小,可能就只有几十字节。

ssh就会产生这样的较小的报文数据,对一个ssh连接,当我们输入一个交互命令后,每个交互按键通常都会生成一个单独的数据包(若用户的输入速度较快,也有可能每个包包含多个字符,但整体报文仍较小),即每个按键是独立传输的,即每次一个字符而非每次一行。另外,ssh会在远程服务器端调用一个shell,对客户端的输入字符做出回显。

如上图所示,ssh发送一次数据,远程服务端会回复确认以及回信数据包,典型的TCP讲这两者的传输结合。

延时确认(Delayed Ack)

一般来说TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段可以实现该功能。累积确认可以允许TCP延迟一小段时间再发送ACK,从而可以将ACK和同方向上的其他需要传的数据结合发送。当然,TCP延迟的时间不能过长,否则对方会误认为数据丢失而出现不必要的重传。一般TCP实际时延最大取200ms。

很明显,采用延时ACK的方法可以减少ACK传输的数目(减少纯ACK包),可以一定程度的减轻网络负载。基于不同的操作系统,延迟发送ACK的最大时延可以动态配置。Linux使用了动态调节算法,可以在快速ack(TCP_QUICKACK)和延时ACK之间切换。

Nagle算法

由上文可知,在ssh连接中,通常单次点击就会引发数据流的传输。如果使用IPV4,一次按键就会产生88字节的TCP包(使用加密和认证),这其中包括20字节的IP头部,20字节的TCP头部,48字节的数据包。众多的小数据包会影响网络的性能。Nagle算法就是在这种情况下的一种解决方法。

Nagle 算法的基本思想是,当一个TCP连接中有在传数据(即已发送但还未确认的数据),小报文段(<SMSS)就不能发送,直到所有的在传数据都收到ACK。并且在收到ACK后,TCP需要收集这些小数据,将其整合到一个报文段中发送。Nagle 算法强制TCP遵循停等策略,即只有等待收到所有在传数据的ACK后才能继续发送。可以理解为该算法实现了自时钟控制:ACK返回越快,数据传输也越快。在相对高延迟的广域网中,减少了发送报文的数目。

但是在小数据包的情况下,例如上述的ssh应用,使用Nagle算法 没发送一个请求和响应包需要等待一个RTT,这就加长了整个传输过程时延。Nagle算法的停等行为,在任一给定时刻,只有一个方向保持传输状态,即任一时刻只有一个包在传。

延时ACK与Nagle算法结合

若将延时ACK与Nagle算法直接结合使用,很可能会影响传输性能。

如上图所示,客户端使用延时ACK发送一个对服务器的请求,而服务器端的相应数据并不适合在同一个包中传输。可以看到由于延时ACK策略,在接收到服务器端的两个相应数据包后,客户端并不立即发送ACK,而是处于等待状态,希望有数据一同捎带发送。而服务器端由于开启了Nagle算法,知道收到客户端的ACK前都不会再发送新数据。此时就形成了一个短暂的死锁,只能等到延时ACK计时器超时,客户端发送ACK,继而服务器发送新数据包。在死锁期间整个传输连接处于空闲状态,使性能降低。所以此时可以禁用Nagle算法。(设置 TCP_NODELAY 选项)

流量控制与窗口管理

我们知道,除了连接建立之初的包交换,每个TCP报文段都包含一个有序的序列号字段、一个ACK号或确认字段,以及一个窗口大小字段。窗口字段即包含窗口通告信息。

窗口通告信息的数值表示发送该窗口信息的通信方为即将到来的新数据预留的存储空间。当TCP应用程序空闲时,就会排队处理这些数据,致使窗口大小字段保持不变。当系统处理速度较慢,或者程序忙于执行其他操作,到来的数据返回ACK后,就需要排队等待被处理,这时新数据的可用存储空间就会减小,窗口的大小也即会减小。若应用程序一直不处理这些排队等待的数据,TCP就必须采用策略限制以至于使发送端停止新数据的发送(若通告窗口为0,没有存储空间)。

每个TCP头部的窗口大小字段表明接收端可用缓存空间的大小,以字节为单位,长度为16位, 但在窗口缩放选项可用大于65535的值。、

滑动窗口

作为全双工传输协议,TCP连接的每一段都可收发数据。每个TCP活动连接的两端都维护一个发送窗口结构和接收窗口结构。

TCP以字节为单位维护其窗口结构。 可用窗口计算值为提供窗口大小减去在传(已发送但未得到确认)的数据值,即(SND.UNA+SND.WND-SND.NXT)。随着时间的推移,当接收到返回的数据ACK,滑动窗口也随之右移。

滑动窗口需要注意的:窗口左边界不能左移,因为它控制的是已确认的ACK号,具有累积性,当然不能返回。当左右边界相等时,称之为零窗口。此时发送端不能再发送新数据。这种情况下,TCP发送端会开始探测对方窗口。

接收端也维护一个窗口结构,该窗口结构记录了已接收并确认的数据,以及它能够接收的最大序列号。

零窗口和TCP持续计时器

由上文可知,通告窗口指示了接收端可接收的数据量。当窗口值变为0时,可以有效阻止发送端继续发送,直到窗口大小恢复为非零值。当接收端重新获得可用空间时,会给发送端传输一个窗口更新,通知其可以继续发送数据。这样的窗口更新通知一般都为纯ACK包,不能保证其传输的可靠性。

因此TCP设计了持续计时器,发送端使用持续计时器间歇性的查询接收端,看其窗口是否已增长。持续计时器会触发窗口探测的传输,强制要求接收端返回包含窗口大小的ACK包。

窗口探测包含一个字节的数据,采用TCP可靠传输,当TCP持续计时器超时,就会触发窗口探测的发送。与TCP重传计时器类似,该计时器也可以采用指数时间退避来计算持续计时器的超时。

糊涂窗口综合征

基于窗口的流量控制机制,有可能会出现交换数据段大小不是全长的而是一些较小的数据段。这种现象被称为糊涂窗口综合征(Silly Window Syndrome,SWS),由于每个报文段中有用数据相对于头部信息的比例较小,因此耗费的资源也更多,传输效率也越低。

SWS现象可能是由TCP连接的两端导致的:

  • 接收端通告窗口较小,没有等到窗口变大才通告
  • 发送端发送的数据段较小,没有等到将其他数据组合成一个更大的报文段。

要避免SWS,必须在发送端或接收端实现相关的规则:

  1. 对于接收端,不应通告小的窗口值。在窗口可增至一个全长的报文段(即接收端MSS)或者接收端缓存空间的一半(取两者中较小者)之前,不能通告比当前窗口更大的窗口值。

  2. 对于发送端,不应发送小的报文段,而且需由Nagle算法控制发送,更具体地,为避免SWS问题,只有至少满足以下条件之一时才能传输报文段:

    a)全长(MSS)的报文段可以发送

    b)数据段长度≥接收端通告过的最大窗口值的一半

    c)某一ACK不是目前期盼的(即没有未经确认的在传数据),或该连接禁用Nagle算法

由上可知,我们之前讨论的使用Nagle算法阻止发送小的报文段,这个“小报文”意味着字节数要小于SMSS(即不超过PMTU或接收端MSS的最大包大小)。

大容量缓存与自动调优

由上面的分析可知,在相似的环境下,TCP应用的缓存越小,吞吐性能越差,而且受TCP连接两端的影响,即时接收端指定一个足够大的缓存,发送端也可能指定一个很小的缓存,最终导致性能变差。因此很多TCP协议栈中上层应用不能指定接收缓存的大小。在多数情况下,上层应用指定的缓存会被忽视,而由操作系统来指定一个较大的固定值或动态变化的计算值。

紧急机制

TCP头部有一个位字段URG用来指定“紧急数据”。应用要执行写操作时,可通过设置Berkeley套接字API(MSG_OOB)的特殊选项将数据标记为紧急。当发送端TCP收到这类写操作要求时,会进入紧急模式的特殊状态。 它记录紧急数据的最后一个字节,用于设置紧急指针字段,随后发送端生成的每个TCP头部都包含该字段,知道应用停止紧急数据写操作,并且所有序列号大于紧急指针的数据都经接收端确认。

以上是关于TCP数据流与窗口管理的主要内容,如果未能解决你的问题,请参考以下文章

学习Wireshark:TCP窗口与拥塞处理

TCP系列35—窗口管理&流控—9紧急机制

TCP系列33—窗口管理&流控—7Silly Window Syndrome(SWS)

TCP系列36—窗口管理&流控—10linux下的异常报文系列接收

TCP的滑动窗口与拥塞窗口

13.TCP的超时与重传