TCP已经在从1200 b/s的拨号SLIP链路到以太数据链路上运行了许多年。在80年代和90年代初期,以太网是运行TCP/IP最主要的数据链路方式。虽然TCP在比以太网速率高的环境(如T2电话线、FDDI及千兆比网络)中也能够正确运行,但在这些高速率环境下, TCP的某些限制就会暴露出来。
本章讨论TCP的一些修改建议,这些建议可以使TCP在高速率环境中获得最大的吞吐量。首先要讨论前面已经碰到过的路径MTU发现机制,本章主要关注它如何与TCP协同工作。这个机制通常可以使TCP为非本地的连接使用大于536字节的MTU,从而增加吞吐量。
接着介绍长肥管道(long fat pipe),也就是那些具有很大的带宽时延乘积的网络,以及TCP在这些网络上所具有的局限性。为处理长肥管道,我们描述两个新的TCP选项:窗口扩大选项(用来增加TCP的最大窗口,使之超过65535字节)和时间戳选项。后面这个选项可以使TCP对报文段进行更加精确的RTT测量,还可以在高速率下对可能发生的序号回绕提供保护。
我们还将介绍建议的T/TCP,这是为增加事务功能而对TCP进行的修改。通信的事务模式以客户的请求将被服务器应答的响应为主要特征。这是客户服务器计算的常见模型。T/TCP的目的就是减少两端交换的报文段数量,避免三次握手和使用4个报文段进行连接的关闭,从而使客户可以在一个RTT和处理请求所必需的时间内收到服务器的应答。
这些新选项(路径MTU发现、窗口扩大选项、时间戳选项和T/TCP)中令人印象最深刻的就是它们与现有的TCP实现能够向后兼容,即包括这些新选项的系统仍然可以与原有的旧系统进行交互。除了在一个ICMP报文中为路径MTU发现增加了一个额外字段之外,这些新的选项只需要在那些需要使用它们的端系统中进行实现。
路径MTU是当前在两个主机之间的路径上任何网络上的最小MTU。路径MTU发现在IP首部中继承并设置“不要分片(DF)”比特,来发现当前路径上的路由器是否需要对正在发送的IP数据报进行分片。如果一个待转发的IP数据报被设置DF比特,而其长度又超过了MTU,那么路由器将返回ICMP不可达的差错。在11.7节我们显示了某版本的traceroute程序使用该机制来决定目的地的路径MTU。在11.8节我们看到UDP是怎样处理路径MTU发现的。在本节我们将讨论这个机制是如何按照RFC 1191中规定的那样在TCP中进行使用的。
TCP的路径MTU发现按如下方式进行:在连接建立时, TCP使用输出接口或对端声明的MSS中的最小MTU作为起始的报文段大小。路径MTU发现不允许TCP超过对端声明的MSS。如果对端没有指定一个MSS,则默认为536。一旦选定了起始的报文段大小,在该连接上的所有被TCP发送的IP数据报都将被设置DF比特。如果某个中间路由器需要对一个设置了DF标志的数据报进行分片,它就丢弃这个数据报,并产生一个ICMP的“不能分片”差错。如果收到这个ICMP差错, TCP就减少段大小并进行重传。如果路由器产生的是一个较新的该类ICMP差错,则报文段大小被设置为下一跳的MTU减去IP和TCP的首部长度。如果是一个较旧的该类ICMP差错,则必须尝试下一个可能的最小MTU。当由这个ICMP差错引起的重传发生时,拥塞窗口不需要变化,但要启动慢启动。
由于路由可以动态变化,因此在最后一次减少路径MTU的一段时间以后,可以尝试使用一个较大的值(直到等于对端声明的MSS或输出接口MTU的最小值)。RFC 1191推荐这个时间间隔为10分钟。
在对非本地目的地,默认的MSS通常为536字节,路径MTU发现可以避免在通过MTU小于576(这非常罕见)的中间链路时进行分片。对于本地目的主机,也可以避免在中间链路(如以太网)的MTU小于端点网络(如令牌环网)的情况下进行分片。但为了能使路径MTU更加有用和充分利用MTU大于576的广域网,一个实现必须停止使用为非本地目的制定的536的MTU默认值。MSS的一个较好的选择是输出接口的MTU(当然要减去IP和TCP的首部大小)。
我们在solaris上运行sock程序并向slip上的丢弃服务器进行一个512字节的写操作:
solaris % sock -i -n1 -w512 slip discard
在主机sun的SLIP接口上收集的tcpdump的输出结果如下:
在[Bellovin 1993]中的测量表明,分组并不一定是越大越好。考虑下面的例子。我们通过4个路由器(R1-R2-R3-R4)发送8192个字节,每个路由器与一个T1电话线(1544000 b/s)相连。
首先我们使用两个4096字节的分组,从R1到R4它需要花费4个单位时间来发送所有的8192字节。每一跳的时间为 ( 4096 B + 40 B ) × 8 b / B 1544000 b / s = 21.4 m s / 跳 \frac{(4096B + 40B) \times 8b/B}{1544000 b/s} = 21.4ms/跳 1544000b/s(4096B+40B)×8b/B=21.4ms/跳(将TCP和IP的首部算为40字节)。发送数据的整个时间为分组个数加上跳数减1,从图中可以看到是4个单位时间,或85.6ms。每个链路空闲2个单位时间,或42.8ms。
当我们发送16个512字节的分组时,这将花费更多的单位时间,但是由于发送的分组较短,因此每个单位时间较小。 ( 512 B + 40 B ) × 8 b / B 1544000 b / s = 2.9 m s / 跳 \frac{(512B + 40B) \times 8b/B}{1544000 b/s} = 2.9ms/跳 1544000b/s(512B+40B)×8b/B=2.9ms/跳现在总时间为 18 × 2.9 = 52.2 m s 18 \times 2.9 = 52.2 ms 18×2.9=52.2ms。每个链路也空闲2个单位的时间,即5.8ms。
在20.7节,我们把一个连接的容量表示为 c a p a c i t y ( b ) = b a n d w i d t h ( b / s ) × r o u n d − t r i p t i m e ( s ) capacity(b) = bandwidth(b/s) \times round \!-\! trip\: time(s) capacity(b)=bandwidth(b/s)×round−triptime(s)并称之为带宽时延乘积。也可称它为两端的管道大小。当这个乘积变得越来越大时, TCP的某些局限性就会暴露出来。
具有大的带宽时延乘积的网络被称为长肥网络( Long Fat Network ,即LFN,发音为“elefan(t)s”),而一个运行在LFN上的TCP连接被称为长肥管道。使用长肥管道会遇到多种问题。
窗口扩大选项使TCP的窗口定义从16 bit增加为32 bit。这并不是通过修改TCP首部来实现的, TCP首部仍然使用16 bit ,而是通过定义一个TCP选项实现对16 bit 的扩大操作(scaling operation)来完成的。于是TCP在内部将实际的窗口大小维持为32 bit的值。在图18-20可以看到关于这个选项的例子。一个字节的移位记数器取值为0(没有扩大窗口的操作)和14。最大值14表示窗口大小为1073725440字节( 65535 × 2 14 65535\times2^{14} 65535×214)。
这个选项只能够出现在一个SYN报文段中,因此当连接建立起来后,在每个方向的扩大因子是固定的。为了使用窗口扩大,两端必须在它们的SYN报文段中发送这个选项。主动建立连接的一方在其SYN中发送这个选项,但是被动建立连接的一方只能够在收到带有这个选项的SYN之后才可以发送这个选项。每个方向上的扩大因子可以不同。如果主动连接的一方发送一个非零的扩大因子,但是没有从另一端收到一个窗口扩大选项,它就将发送和接收的移位记数器置为0。这就允许较新的系统能够与较旧的、不理解新选项的系统进行互操作。
假定我们正在使用窗口扩大选项,发送移位记数为S,而接收移位记数则为R。于是我们从另一端收到的每一个16 bit的通告窗口将被左移R位以获得实际的通告窗口大小。每次当我们向对方发送一个窗口通告的时候,我们将实际的32 bit窗口大小右移S比特,然后用它来替换TCP首部中的16 bit的值。TCP根据接收缓存的大小自动选择移位计数。这个大小由系统设置,但是通常向应用进程提供了修改途径。
时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,从而允许发送方为每一个收到的ACK计算RTT。图18-20显示了时间戳选项的格式。发送方在第1个字段中放置一个32 bit的值,接收方在应答字段中回显这个数值。包含这个选项的TCP首部长度将从正常的20字节增加为32字节。时间戳是一个单调递增的值。由于接收方只需要回显收到的内容,因此不需要关注时间戳单元是什么。这个选项不需要在两个主机之间进行任何形式的时钟同步。主动发起连接的一方在它的SYN中指定选项。
为了减少任一端所维持的状态数量,对于每个连接只保持一个时间戳的数值。选择何时更新这个数值的算法非常简单:
这个算法能够处理下面两种情况:
PAWS算法不需要在发送方和接收方之间进行任何形式的时间同步。接收方所需要的就是时间戳的值应该单调递增,并且每个窗口至少增加1。
接收方将时间戳视为序列号的一个32 bit的扩展。假定一个报文段在时间B丢失并被重传。还假定这个丢失的报文段在时间E重新出现。由于在时间E重新出现的报文段的时间戳为2,这比最近有效的时间戳小(5或6),因此PAWS算法将其丢弃。
一个事务(transaction)就是符合下面这些特征的一个客户请求及其随后的服务器响应。
TCP提供了过多的事务特征,而UDP提供的则不够。通常应用程序使用UDP来构造(避免TCP连接的开销),而许多需要的特征(如动态超时和重传、拥塞避免等)被放置在应用层,一遍又一遍的重新设计和实现。一个较好的解决方法是提供一个能够提供足够多的事务处理功能的运输层。
TCP为处理事务而需要进行的两个改动是避免三次握手和缩短WAIT_TIME状态。T/TCP通过使用加速打开来避免三次握手:
这种“加速打开”避免了使用三次握手的要求,除非客户或者服务器已经崩溃并重新启动。这样做的代价是服务器必须记住从每个客户接收的最近的CC值。
基于在两个主机之间测量RTT来动态计算TIME_WAIT的延时,可以缩短TIME_WAIT状态。TIME_WAIT时延被设置为8倍的重传超时值RTO。
一个可作为替换的事务协议是通用报文事务协议(Versatile Message Transaction Protocol, VMTP),该协议在RFC 1045中进行了描述。与T/TCP是现有协议的一个小的扩充不同,VMTP是使用IP的一个完整的运输层。VMTP处理差错检测、重传和重复压缩。它还支持多播通信。