TCP数据流与窗口管理

来源:互联网 发布:逆战神灯源码 编辑:程序博客网 时间:2024/06/07 03:06
交互式通信
TCP流量中,大约90%或者更多的TCP报文段都包含大批量数据(如Web、文件共享、电子邮件、备份),其余部分则包含交互式数据(如远程登录、网络游戏)。批量数据段通常较大(1500字节或者更大),而交互式数据段则会比较小(几十字节的用户数据)。


ssh(安全外壳协议)是具备较强安全性(基于密码学的加密和认证)的远程登录协议,已经基本取代了早期的Telnet(远程登录服务)。对于一个ssh连接,当我们输入一个交互命令后的数据流。客户端获取用户输入信息,然后将其传给服务器端。服务器端对命令进行解释并生成响应返回给客户端。客户端对其传输数据加密,意味着用户输入的信息在通过连接传输前已经进行了加密。


每个交互按键通常都会生成一个单独的数据包。每个按键都是独立传输的,ssh会在远程系统(服务器端)调用一个shell(命令解释器),对客户端的输入字符做出回显。因此,每个输入的字符会生成4个TCP数据段:客户端的交互击键输入、服务器端对击键的确认、服务器端生成的回显、客户端对该回显得确认。


延时确认
TCP并不对每个到来的数据包都返回ACK,利用TCP的累积ACK字段就能实现该功能。累积确认可以允许TCP延迟一段时间发送ACK,以便将ACK和相同方向上需要传的数据结合发送。这种捎带传输的方法经常用于批量数据传输。
采用延时ACK的方法会减少ACK传输数目,可以一定程度上地减轻网络负载。

Nagle算法

在ssh连接中,通常单次击键就会引发数据流的传输。如果使用IPv4,一次击键会生成约88字节大小的TCP/IPv4包:20字节的IP头部、20字节的TCP头部,数据部分为48字节。这些小包会造成相当高的网络传输代价。对于大部分局域网来说不存在拥塞,但是对于广域网来说会加重拥塞,严重影响网络性能。一种简单有效的解决方法----Nagle算法。


Nagle算法要求,当一个TCP连接中有在传数据(即那些已发送但还未经确认的数据),小的报文段(长度小于SMSS)就不能被发送,直到所有的在传数据都收到ACK。并且,在收到ACK之后,TCP需要收集这些小数据,将其整合到一个报文段中发送。这种方法迫使TCP遵循停等规程----只有等接收到所有在传数据的ACK后才能继续发送。其实现了自时钟控制:ACK返回越快,数据传输也越快。


其实Nagle算法做出了一种折中,传输的包数目更少而长度更大,但同时传输时延也更长。
若将延时ACK与Nagle算法直接结合使用效果可能不好。例如在接收到服务器端的两个包后,客户端并不立即发送ACK,而是出于等待状态,希望有数据一同捎带发送。通常情况下,TCP在接收到两个全长的数据包后就应返回一个ACK,但这里并非如此,在服务器端由于使用了Nagle算法,直到收到ACK前都不能发送新数据,因为任一时刻只允许至多一个包在传。因此延时ACK与Nagle算法的结合导致了某种程度的死锁(两端互相等待对方做出行动)。当然这种死锁并不是永久的,在延时ACK计时器超时后死锁就会接触。
禁用Nagle算法:若某个应用使用Berkeley套接字API,可以设置TCP_NODELAY选项。


流量控制和窗口管理
每个TCP报文段(除了连接建立之初的包交换)都包含一个有效的序列号字段/一个ACK号字段或确认字段,以及一个窗口大小字段(包含窗口通告信息)。
每个TCP头部的窗口大小字段表明接收端可用缓存空间的大小,以字节为单位。该字段长度为16位,但窗口缩放选项可用于大于65536的值。

滑动窗口
TCP连接的每一端都可收发数据。连接的收发数据量是通过一组窗口结构来维护的。每个TCP活动连接的两端都维护一个发送窗口结构和接收窗口结构。下图是一个假设的TCP发送窗口结构。



TCP以字节为单位维护其窗口结构。在上图中,我们已标号为2-11字节。由接收端通告的窗口称为提供窗口,包含4-9字节。接收端已成功确认包括第3字节在内的之前的数据,并通告了一个6字节大小的窗口。窗口大小字段相对ACK有一个字节的偏移量。发送端计算其可用窗口,即它可以立即发送的数据量。可用窗口计算值为提供窗口大小减去在传(已发送但还未确认)的数据值。变量SND.UNA和SND.WND分别记录窗口左边界和提供窗口值。SND.NXT则记录下次发送的数据序列号,因此可用窗口值等于(SND.UNA+SND.WND-SND.NXT)。


随着时间的推移,当接收到返回的数据ACK,滑动窗口也随之右移。窗口两端的相对运动使得窗口增大或减小。可用三个术语来描述窗口左右边界的运动:

1.关闭,即窗口左边界右移。当已发送数据得到ACK确认时,窗口会减小。
2.打开,即窗口右边界右移,使得可发送数据量增大。当已确认数据得到处理,接收端可用缓存变大,窗口也随之变大。
3.收缩,即窗口右边界左移。


每个TCP报文段都包含ACK号和窗口通告信息,TCP发送端可以据此调节窗口结构。窗口左边界不能左移,因为它控制的是已确认的ACK号,具有累积性,不能返回。当得到的ACK号增大而窗口大小保持不变时,我们就说窗口向前滑动。若随着ACK号增大而窗口却减小,则左右边界距离减小。当左右边界相等时,称之为零窗口。此时发送端不能再发送新数据。这种情况下,TCP发送端开始探测对方窗口,伺机增大提供窗口。


接收端也维护一个窗口结构,但比发送端窗口简单。该窗口记录了已接收并确认的数据,以及它能接收的最大序列号。该窗口可以保证其接收数据的正确性。特别是,接收端希望避免存储重复的已接收和确认数据,以及避免存储不应接收的数据。下图描述了接收窗口结构:



与发送端窗口一样,该窗口结构也包含一个左边界和右边界,但窗口内的字节(图中的4-9字节)并没有区分。对接收端来说,到达序列号小于左窗口边界(称为RCV.NXT),被认为是重复数据而丢弃,超过右边界(RCV.WND+RCV.NXT)的则超出处理范围,也被丢弃。注意到由于TCP的累积ACK结构,只有当到达数据序列号等于左边界时,数据才不会被丢弃,窗口才能向前滑动。对于选择确认TCP来说,使用SACK选项,窗口内的其他报文段也可以被接收确认,但只有在接收到等于左边界的序列号数据时,窗口才能前移。


零窗口与TCP持续计时器
TCP是通过接收端的通告窗口来实现流量控制的。通告窗口指示了接收端可接收的数据量。当窗口值变为0时,可以有效阻止发送端继续发送,直到窗口大小恢复为非零值。当接收端重新获得可用空间时,会给发送端传输一个窗口更新,告知其可继续发送数据,这样的窗口更新通常都不包含数据(为纯ACK),不能保证其传输的可靠性。因此TCP必须有相应的措施处理这类丢包。


如果一个包含窗口更新的ACK丢失,通信双方就会一直处于等待状态:接收方等待接收数据,发送方等待收到窗口更新告知其可继续发送。为防止这种死锁的发生,发送端会采用一个持续计时器间歇性查询接收端,看其窗口是否已增长,持续计时器会触发窗口探测的传输,强制要求接收端返回ACK(其中包含了窗口大小字段)。

窗口探测包含一个字节的数据,采用TCP可靠重传,因此可以避免由窗口更新丢失导致的死锁。当TCP持续计时器超时,就会触发窗口探测的发送。


糊涂窗口综合征

基于窗口的流量控制机制,尤其是不使用大小固定的报文段的情况(如TCP),可能会出现糊涂窗口综合征(SWS)的缺陷。当出现该问题时,交换数据段大小不是全长的而是一些较小的数据段。由于每个报文段中有用数据相对于头部信息的比例较小,因此耗费的资源也更多,相应地传输效率也更低。


TCP连接两端都有可能导致SWS的出现:接收端的窗口通告较小(没有等到窗口变大才通告),或者发送端发送的数据段较(没有等待将其他数据组合成一个更大的报文段)。要避免SWS问题,必须在发送端或接收端实现相关规则。TCP无法提前预知某一端的行为。需要遵循以下规则:

1.对于接收端来说,不应通告小的窗口值。
2.对于发送端来说,不应发送小的报文段,而且需要由Nagle算法控制何时发送。为避免SWS问题,只有至少满足以下条件之一时才能传输报文段:
(a)全长的报文段可以发送
(b)数据段长度大于等于接收端通告过的最大窗口值的一半的,可以发送。
(c)满足以下任一条件的都可以发送:(i)某一ACK不是目前期盼的(即没有未经确认的在传数据)(ii)该连接禁用Nagle算法
原创粉丝点击