TCP/IP详解:TCP——数据流与窗口管理

1、PSH位
PSH标志位通常表示发送端缓存为空,也就是说,当PSH置位的数据包发送完成后,发送端没有其他数据包需要传输。

2、延时确认
很多情况下TCP并不是对每个到来的数据包都返回ACK,而是在累计几个包后返回一个ACK,实践中最大时延一般取200ms,对于批量数据传输一般是2:1的比例,ACK计时器超时时间以及超市前在传的ACK数目都可以配置。

3、Nagle算法
Nagle算法要求,当一个TCP连接中有在传数据(即那些已发送但还未经确认的数据),小的报文段(长度小于SMSS)就不能被发送,知道所有的在传数据都收到ACK。并且,在收到ACK后,TCP需要收集这些小数据,将其整合到一个报文段中发送。这种方法迫使TCP遵循停等(stop-and-wait)规程——只有等接收到所有在传数据到的ACK后才能继续发送。
Nagle算法做出了一种折中:传输的包数目更少而长度更大,但同时传输时延也更长。
在有些情况下并不适用Nagle,典型的包括那些要求时延尽量小的应用,如多人网络游戏。socket API可以通过设置TCP_NODELAY选项来禁用Nagle算法。

4、滑动窗口
每个TCP活动连接的两端都维护了一个发送窗口结构和一个接收窗口结构
TCP/IP详解:TCP——数据流与窗口管理
上图是一个发送窗口结构。提供窗口大小是由接收端通告的窗口决定的。上图看出,1,2,3字节已成功发送,4,5,6字节已发送但未经确认,即未收到ACK,7,8,9是接下来可以继续发送的数据。10,11是暂时还不能发送的数据,因为接收端没有空间接收这些数据。待收到4,5,6的确认ACK后,窗口会根据通告的窗口大小整体向右滑动。
TCP/IP详解:TCP——数据流与窗口管理
上图是接收端维护的接收窗口

5、零窗口与TCP持续计时器
当发送端窗口的左右边界相等时,称之为零窗口,此时发送端不能再发送新数据。这种情况下,TCP发送开始探测(probe)对方窗口。当接收端重新获得可用空间时,会给发送端传输一个窗口更新(window update),告知其可继续发送数据。这样的窗口更新通常不包含数据(为“纯ACK”),不能保证其传输的可靠性。

注意这里隐藏的知识点:并不是所有的TCP包都能被可靠传输,不带数据的纯ACK包不能保证这种可靠性。

TCP必须有相应的措施能处理这类丢包。如果一个包含窗口更新的ACK丢失,通信双方就会一直处于等待状态:接收方等待接收数据(其已将窗口设为非零值),发送方等待收到窗口更新告知其可继续发送。为防止这种死锁的发生,发送端会采用一个持续计时器间歇性的查询接收端,看其窗口是否已增长。持续计时器会触发窗口探测(window probe)的传输,强制要求接收端返回ACK。
窗口探测的发送时间间隔采用的也是二进制指数退避策略。
窗口探测不像窗口更新,它发送的数据包包含一个字节的数据,采用TCP可靠传输,因此可以避免因窗口更新丢包导致的死锁的问题。
窗口探测虽然也是采用的二进制指数时间退避策略,但通常TCP不会停止发送窗口探测,由此可能会放弃执行重传操作。这种情况可能导致某种程度的资源耗尽。

 6、糊涂窗口综合征(SWS)
SWS(Silly Window Syndrome)缺陷是指交换的数据段大小不是全长(一个MSS)的而是一些较小的数据段,由于每个报文段中的有用数据相对于头部信息的比例较小,因此耗费的资源也更多,相应的传输效率也更低。
TCP连接的两端都可能导致SWS的出现:接收端的通告窗口较小或者发送端发送的数据小。要避免SWS,必须在发送端或接收端实现相关规则:

  • 对于接收端,不应通告小的窗口值。[RFC1122]描述的接收算法中,在窗口可增长至一个全长的报文段(即接收端MSS)或者接收端缓存空间的一半(两者取小)之前,不能通告比当前窗口更大的窗口值。可能有两种情况会用到该规则:当应用程序处理接收到的数据后使得可用缓存增大(但还没达到上述规则)以及TCP接收端需要强制返回对窗口探测的响应。
  • 对于发送端来说,不应发送小的报文段,而且需由Nagle算法控制何时发送。为避免SWS问题,只有至少满足一下条件之一才能发送报文段。
    (a)全长的报文段可以发送
    (b)数据段长度>=接收端通告过的最大窗口值的一半,可以发送
    (c)满足以下任一条件可以发送:某一ACK不是目前期盼的(即没有未经确认的在传数据)或该连接禁用Nagle算法。

原书中15.5.3.1的例子可以很好的验证上述规则。

7、大容量缓存和自动调优
有前面的知识可以看到,接收端或发送端较小的缓存会影响TCP应用的吞吐性能。因此很多TCP协议栈中上层应用不能指定接收缓存大小。多数情况下,上层应用指定的缓存也会被忽视,而是由系统来指定一个较大的固定的值或动态变化的计算值。
在较新的Windows(Vista/7)和Linux中,支持接收窗口的自动调优。

8、紧急机制
TCP头部字段中的URG字段用来表示紧急数据,在该字段被置位的情况下,紧急指针指向紧急数据的下一个字节。如紧急数据的字节号为9,紧急指针指向的数据的字节号为10。因为只有一个紧急指针,所以紧急数据只有一个字节。
发送端在调用socketAPI send(sockfd,"X",1,MSG_OOB)后,(MSG_OOB标记这是一个紧急数据),这时紧急数据可能还在发送缓冲区排队发送,如果在接下来的一个包中就能就紧急数据发出去,那么在这个包收到确认ack后,之后发送的包中的urg位都不会被置位了,相反,如果紧急数据排在后面,需要前面发送好几个包后才能轮到它,那么自它被加入到缓冲区开始,每个发送的包的urg都会被置位,不管这个包里有没有它,直到带着它的包被确认。
我是这么理解的,很多资料上都没提到这一点,紧急数据虽被称为带外数据(Out-Of-Band OOB),但是它并会通过额外的连接传输,也不会插队发送,它只是告诉接收端,我这个数据跟当前连接上的其他数据不太一样,你需要特殊处理。
 

 

上一篇:Wireshark数据抓包分析之传输层协议(TCP协议)


下一篇:OSI 7层模型