TCP选项

       TCP首部可以由多达40字节的可选信息。选项用于把附加信息传递给终点,或用来填充对齐其他选项。我们将定义两大类选项:1字节选项和多字节选项。第一类选项包括两种选项:选项列表结束和误操作。在大多数实现中,第二类选项包括了五种选项:最大报文段长度、窗口扩大因子、时间戳、允许SACK和SACK,见下图。

选项结束(EOF)

       选项结束(end of option,EOP)选项是1字节选项,用来在选项区的结尾处进行填充。它只能用作最后一个选项。这个选项只允许出现一次。在这个选项后,接收方就要检查有效载荷数据了。下图给出了一个雷子,在首部后面后一个3字节的选项,在这个选项后面紧跟着的是数据。因此要用一个EOF选项插入进来使用数据对齐下一个字的开头。

EOF只能使用一次。

EOF选项向终点传达了两种信息:

1、首部中没有更多的选项了。

2、从应用程序传递来的数据开始于下一个32位字开始的地方。

无操作(NOP)

       无操作(no-operation,NOP)选项也是1字节选项,用作选项之间的填充。但是,它通常用在另一个选项之前,帮助选项能够在4字--格中对齐。例如:在下图中,它被用来对齐一个3字节的选项(如窗口扩缩因子)和一个10字节的选项(如时间戳)。NOP可多次使用。

最大报文段长度(MSS)

      最大报文段长度选项(maximun-segment-size option)定义了能够被终点接收的TCP报文段的最大数据单元。虽然它的名字是这样,但它定义的是数据的最大长度,而不是报文段的最大长度。因为这个字段是16位长,所以这个值只能在0到65535字节之间。下图给出了这个选项格式。

MSS是在连接建立阶段确定的。每一方都要定义它在连接期间接收的报文段的MSS。若有一方没有定义MSS的大小,则使用默认值,即536字节。MSS的值是在连接建立阶段确定的,在连接期间保持不变。

窗口扩大因子

       首部中的窗口大小字段定义了滑动窗口大小。这个字段的长度是16位,表示这个窗口的范围可以从0~65535字节。虽然看起来这是一个非常大的窗口,但它还是有可能不够用,特别是当数据在一个长租管道(即距离很远且宽带很大的信道)中传送时。

        为了加大这个窗口大小,就要使用窗口扩大因子(window scale factor)。新的窗口大小可以这样求出,即先计算2的窗口扩大因子次方,再把得出的结果乘以首部中的窗口值。

        新的窗口值 = 首部中定义的窗口值 x 2^窗口扩大因子

        下图给出了窗口扩大因子选项格式。

       扩大因子有时称为移位计数,因为要把一个数乘以2的几次方,只要把这个数按位操作向左漂移几位就行。换言之,窗口大小的实际值可以这样确定:把分组中所显示的窗口值向左移动窗口扩大因子这样多的位数即可。

       例如:假定窗口扩大因子的值为3.一个端点收到的确认所给出的窗口大小是32768,那么它可用的窗口大小就是32768X2^3或262144字节。把32768向左移动3位也可以得到同样的数值。

       虽然扩大因子最高可达到255,但TCP/IP所容许的最大值是14,这就表示最大窗口值可以使2 ^16 x 2^14=2^30,它小于序号的最大值。请注意,窗口的大小不能超过序号的最大值。

        窗口扩大因子只能在连接建立阶段确定,在连接期间它的值不能改变。在数据传送阶段,窗口大小(在首部中指明)可以改变,但它必须乘以相同的扩大因子。请注意,连接的一端可以把窗口扩大因子设置为0,意思是说虽然它支持这个选项,但在目前这个连接中,它不想使用这个选项。

时间戳

       这是一个10字节的选项,其格式如下图所示。请注意主动打开的那一端在连接请求报文段(SYN报文段)中宣布了一个时间戳。如果它从来自对方的下一个报文段(SYN+ACK)中也收到一个时间戳,那么就表示允许使用时间戳,否则就不再使用它。时间戳选项(timestamp option)有两个应用:测量往返时间和防止序号绕回。

测量RTT

        时间戳可用来测量往返时间(RTT)。当TCP准备好发送一个报文段时,就读取系统时钟值,并把32位数值插入到时间戳字段中。接收方在发送对这个报文段的确认包括了这个报文段的字节的积累确认时,就把收到的时间戳复制到时间戳送回答字段中。发送方在收到确认是,就用时钟表示的当前时间减去时间戳回送回答的数值,从而找到RTT。

       请注意,在这里不需要对发送方和接收方的时钟进行同步,因为所有的计算都是基于发送方的时钟。还应该注意的是,发送方不需要记住或存储发送出去的报文段的时间,因为报文段本身就携带了这个时间。

       接收方需要保持两个变量。第一个是lastack,这是最近一次发送的确认号。第二个是tsrecent,这是没有回送的最近一个时间戳。当接收方收到一个所包含的字节号与lastack匹配的报文段是,就把其中的时间戳字段的值保存到tsrecent变量中。在接收方发送对该报文段的确认是,再把tsrecent变量的值插入到回送回答字段中。下图就给出了连接的一端计算往返时间的例子。如果要计算另一端的RTT,那么只需反过来即可。

    发送方只是简单地把时钟值(例如:从物业到现在所经过的秒数)插入到第一个和第二个报文段的时间戳字段中。在收到确认时(第三个报文段),发送方检查其时钟值,然后从当前时钟值减去回送回答字段中的数值。在这种情况得出RTT为12秒。

     接收方的功能要更加复杂些,它要保留上次发送出去的确认号(12000)。当第一个报文段达到是,其中包含的字节序号是12000~12099.第一个字节的序号与lastack的值一样。接收方就把该报文段的时间戳值(4720)复制到tsrecent变量中。lastack的数值仍然是12000(没有新的确认发送出去)。当第二个报文段到达时,因为这个报文段中的字节号没有和lastack数值一致的,因此就忽略它的时间戳字段的值。当接收方决定要发送一个积累确认(确认号为12200)时,就把lastack的数值改为12200,同时把tsrecent的数值插入到回送回答 字段中。此后tsrecent的数值保持不变,直至收到携带字节12200的新报文段(下一个报文段)。

       请注意,本例所给出的,计算出来的RTT是从发送第一个报文段开始到收到第三个报文段为止的时间。这是RTT的真正含义:从一个分组发送出去至收到对它的确认时间。第三个报文携带了对第一个和第二个报文段的确认。

  PAWS

       时间戳选项还有另一个应用,就是防止序号绕回(Protection Against Wrapped Sequence Numbers,PAWS)。在TCP中定义的序号长度是32位。虽然这是一个大数目,但在一个高速的连接中,序号还是有可能发生绕回。这表示如果在某个时间有个序号是n,那么有可能在这同一条连接的生存期中,序号会再次出现n。现在,若第一个报文段被复制到了,而它在第二轮序号中到达,那么属于过去的报文段就有可能错误地被认为是属于新一轮序号的报文段。

       解决这个问题的一个办法就是增加序号的长度,但这会设计到窗口的增大以及报文段格式的改变等等。最简单的解决方法就是在报文段的标志中加入时间戳。换言之,可以用时间戳和序号的组合来标志一个报文段,其实也就是增加了标识的长度。两个报文段400:12001和700:12001明确的属于不同的化身(incarnation),第一个是在时间400发送的,第二个是时间700发送的。

 允许SACK和SACK选项

      TCP报文段中的确认字段被设计为累计确认,也就是说,它报告了收到的最后一个连续的字节,但没有报告已失序到达的那些字节。同样,它对重复的报文段也保持沉默。这些都可能对TCP的性能产生负面影响。如果某些分组丢失或被丢弃,发送方必须等待计时器超时,并重传所有未被确认的报文段。接收方就有可能受到 一些重复的分组。为了改善性能,人们提出了选择确认(SACK)。选择确认使发送方能够更好地知道哪些报文段真正丢失了,而那些报文段已经失序到达了。新的建议甚至还包括了一个重要报文段的清单。如此一来,发送方就可以仅仅发送那些真正的丢失的报文段。重复报文段的清单能够帮助发送方找出那些报文段是由于较短的超时而被重传了。

       这个建议定义了两个新的选项:允许SACK和SACK,如下图。

       两个字节长度的允许SACK选项(SACK-permitted option)只用在连接建立阶段。发送SYN报文段的主机增加这个选项以说明它能够支持SACK选项。如果另一端在它的SYN+ACK报文段中也包含了这个选项,那么双方在数据传送阶段就能够使用SACK选项。请注意,允许SACK选项是不能在数据传送阶段使用。

  

可变长度的SACK选项(SACK option)只能在双方都已同意(即双方已在连接建立阶段交换了允许SACK选项)的前提下,用于数据传送阶段。这个选项包含了一张失序到达的数据块列表。每个块占用两个32位数,他们分别定义了块的开始和结束。就目前而言,只要记住TCP所允许的选项大小仅有40字节,也就是说,SACK选项定义的块不能超过4个。因为5个块的信息要占据(5x2)x4+2或42字节,它超出了报文段的选项区的长度。如果SACK选项和其他选项一起使用,那么块的数目还要减少。

       SACK选项的第一个块可用来报告重复收到的报文段。这仅在具体的实现里允许这个特点时才能使用。

 

举例

        让我们看一下SACK选项是怎样把失序到达的数据块列出的。在下图中,连接的某端收到了五个数据报文段。

        第一个和第二个报文段的顺序是连续的,可以对这两个报文段发送一个积累确认。组是报文段3、4和5都是失序的,在第二个和第三个报文段之间,以及在第四个和第五个报文段之间都是间隙。通过ACK和SACK的组合,可以很容易地让发送方明白这种情况。ACK的值是2001,这表示发送方不需要再担心字节1~2000。SACK有两个快,第一块宣布字节4001~6000已失序到达了。第二块说明字节8000~9000也已失序到达。这就表明字节2001~4000和字节6001~8000丢失或被丢弃了,发送方可以只重传这些字节。

      下图给出了用ACK和SACK的组合可以检测出重复报文段的例子。在这个例子中,我们有一些失序到达的报文段(在一个块中)和一个重复的报文段。为了同时指出失序的和重复的数据,在这种情况下,SACK利用第一个块指出重复的报文段,并利用其他的块指出失序到达的数据。请注意,只有第一个块可被用于重复的数据。人们自然要问,发送方在收到这些ACK和SACK的值时是怎样能够知道第一个块是用来指出重复数据(和前一个例子相比)。答案就在于第一个块的字节是在ACK字段中已经确认过得,因此,这个块指出的必定是重复的数据。

         如果在失序到达的这部分报文段中由一个还是重复的,那会发生什么?在这个例子中,有一个报文段(4001:5000)是重复的。

SACK选项先宣布重复的数据,然后才是失序的块。但这时,重复的块没有被ACK确认。不过由于它是失序块中的一部分(4001:5000是4001:6000的一部分),发送方还是能够知道第一个块指出的是重复数据。

       

猜你喜欢

转载自woodding2008.iteye.com/blog/2341446