路径MTU发现
路径MTU发现在IP首部中继承并设置“不要分片(DF)”比特,来发现当前路径上的路由器是否需要对正在发送的IP数据报进行分片。
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的最小值)。
在对非本地目的地,默认的MSS通常为536字节,路径MTU发现可以避免在通过MTU小于576(这非常罕见)的中间链路时进行分片。对于本地目的主机,也可以避免在中间链路(如以太网)的MTU小于端点网络(如令牌环网)的情况下进行分片。但为了能使路径MTU更加有用和充分利用MTU大于576的广域网,一个实现必须停止使用为非本地目的制定的536的MTU默认值。MSS的一个较好的选择是输出接口的MTU(当然要减去IP和TCP的首部大小)。
大分组还是小分组
常规知识告诉我们较大的分组比较好 ,因为发送较少的大分组比发送较多的小分组“花费”要少(假定分组的大小不足以引起分片,否则会引起其他方面的问题)。这些减少的花费与网络(分组首部负荷)、路由器(选路的决定)和主机(协议处理和设备中断)等有关。但是事实告诉我们分组并不一定是越大越好。我们需要在更多的网络上对该领域进行更多的研究。
长肥管道
具有大的带宽时延乘积的网络被称为长肥网络(Long Fat Network)即LFN,而一个运行在LFN上的TCP连接被称为长肥管道。管道可以被水平拉长(一个长的RTT),或被垂直拉高(较高的带宽),或向两个方向拉伸。使用长肥管道会遇到多种问题。
- TCP首部中窗口大小为16bit,从而将窗口限制在65535个字节内。不过现有的网络需要一个更大的窗口来提供最大的吞吐量。窗口扩大选项可以解决这个问题。
- 在一个长肥网络LFN内的分组丢失会使吞吐量急剧减少。如果只有一个报文段丢失,我们需要利用快速重传和快速恢复算法来使管道避免耗尽。但是即使使用这些算法,在一个窗口内发生的多个分组丢失也会典型地使管道耗尽(如果管道耗尽了,慢启动会使它渐渐填满,但这个过程将需要经过多个RTT)。
- 许多TCP实现对每个窗口的RTT仅进行一次测量。它们并不对每个报文段进行RTT测量。在一个长肥网络LFN上需要更好的RTT测量机制。而时间戳选项,它允许更多的报文段被计时,包括重传。
- TCP对每个字节数据使用一个32bit无符号的序号来进行标识。如果在网络中有一个被延迟一段时间的报文段,它所在的连接已被释放,而一个新的连接在这两个主机之间又建立了。可以使用最大的报文段生存时间(MSL)作为一个实现的参数来阻止这种情况的发生。推荐的MSL的值为2分钟(给出一个240秒的2MSL)在长肥网络LFN上,TCP的序号会碰到一个不同的问题。由于序号空间是有限的,在已经传输了4 294 967 296个字节以后序号会被重用。使用TCP的时间戳选项的PAWS(Protection Against Wrapped Sequence numbers)算法(保护回绕的序号)可以对付这种情况。
窗口扩大选项
口扩大选项使TCP的窗口定义从16bit增加为32bit。这并不是通过修改TCP首部来实现的,TCP首部仍然使用16bit ,而是通过定义一个选项实现对16bit 的扩大操作 ( scalingoperation )来完成的。于是TCP在内部将实际的窗口大小维持为 32 bit的值。
这个选项只能够出现在一个SYN报文段中,因此当连接建立起来后,在每个方向的扩大因子是固定的。为了使用窗口扩大,两端必须在它们的SYN报文段中发送这个选项。主动建立连接的一方在其SYN中发送这个选项,但是被动建立连接的一方只能够在收到带有这个选项的SYN之后才可以发送这个选项。每个方向上的扩大因子可以不同。
如果主动连接的一方发送一个非零的扩大因子,但是没有从另一端收到一个窗口扩大选项,它就将发送和接收的移位记数器置为0。这就允许较新的系统能够与较旧的、不理解新选项的系统进行互操作。
时间戳选项
时间戳选项使发送方在每个报文段中放置一个时间戳值。接收方在确认中返回这个数值,从而允许发送方为每一个收到的ACK计算RTT(“每一个收到的ACK”而不是“每一个报文段”,是因为TCP通常用一个ACK来确认多个报文段)。我们提到过目前许多实现为每一个窗口只计算一个RTT,对于包含8个报文段的窗口而言这是正确的。然而,较大的窗口大小则需要进行更好的RTT计算。
时间戳是一个单调递增的值。由于接收方只需要回显收到的内容,因此不需要关注时间戳单元是什么。这个选项不需要在两个主机之间进行任何形式的时钟同步。
在连接建立阶段,对这个选项的规定与窗口扩大选项类似。主动发起连接的一方在它的SYN中指定选项。只有在它从另一方的SYN中收到了这个选项之后,该选项才会在以后的报文段中进行设置。
为了减少任一端所维持的状态数量,对于每个连接只保持一个时间戳的数值。选择何时更新这个数值的算法非常简单:
- TCP跟踪下一个ACK中将要发送的时间戳的值(一个名为tsrecent的变量)以及最后发送的ACK中的确认序号(一个名为lastack的变量)。这个序号就是接收方期望的序号。
- 当一个包含有字节号lastack的报文段到达时,则该报文段中的时间戳被保存在tsrecent中。
- 无论何时发送一个时间戳选项,tsrecent就作为时间戳回显应答字段被发送,而序号字段被保存在lastack中。
这个算法能够处理下面两种情况:
- 如果ACK被接收方迟延,则作为回显值的时间戳值应该对应于最早被确认的报文段。
- 如果一个收到的报文段虽然在窗口范围内但同时又是失序,这就表明前面的报文段已经丢失。当那个丢失的报文段到达时,它的时间戳(而不是失序的报文段的时间戳)将被回显。
PAWS:防止回绕的序号
PAWS算法不需要在发送方和接收方之间进行任何形式的时间同步。接收方所需要的就是时间戳的值应该单调递增,并且每个窗口至少增加1。
T/TCP:为事务用的TCP扩展
TCP提供的是一种虚电路方式的运输服务。一个连接的生存时间包括三个不同的阶段:建立、数据传输和终止。这种虚电路服务非常适合诸如远程注册和文件传输之类的应用。
还有出现其他的应用进程被设计成使用事务服务。一个事务 ( transaction )就是符合下面这些特征的一个客户请求及其随后的服务器响应。
- 应该避免连接建立和连接终止的开销,在可能的时候,发送一个请求分组并接收一个应答分组。
- 等待时间应当减少到等于RTT与SPT之和。其中RTT (Round-Trip Time )为往返时间,而SPT (Server Processing Time )则是服务器处理请求的时间。
- 服务器应当能够检测出重复的请求,并且当收到一个重复的请求时不重新处理事务(避免重新处理意味着服务器不必再次处理请求,而是返回保存的、与该请求对应的应答)。
如今一个应用程序设计人员面对的一种选择是使用TCP还是UDP。TCP提供了过多的事务特征,而UDP提供的则不够。通常应用程序使用UDP来构造(避免TCP连接的开销),而许多需要的特征(如动态超时和重传、拥塞避免等)被放置在应用层,一遍又一遍的重新设计和实现。
一个较好的解决方法是提供一个能够提供足够多的事务处理功能的运输层。这个事务协议被称为T/TCP。
大多数的TCP需要使用7个报文段来打开和关闭一个连接。现在增加三个报文段:一个对应于请求,一个对应于应答和对请求的确认,第三个对应于对应答的确认。如果额外的控制比特被追加到报文段上—也就是,第1个报文段带有SYN、客户请求和一个FIN—客户仍然能够看到一个2倍的RTT与SPT之和的最小开销(与数据一起发送一个SYN和FIN是合法的;当前的TCP是否能够正确处理它们是另外一个问题)。
另一个与TCP有关的问题是TIME_WAIT状态和它需要的2MSL的等待时间。
TCP为处理事务而需要进行的两个改动是避免三次握手和缩短WAIT_TIME状态。T/TCP通过使用加速打开来避免三次握手:
- 它为打开的连接指定一个32bit的连接计数CC (Connection Count),无论主动打开还是被动打开。一个主机的CC值从一个全局计数器中获得,该计数器每次被使用时加1。
- 在两个使用T/TCP的主机之间的每一个报文段都包括一个新的TCP选项CC。这个选项的长度为6个字节,包含发送方在该连接上的32bit的CC值。
- 一个主机维持一个缓存,该缓存保留每个主机上一次的CC值,这些值从来自这个主机的一个可接受的SYN报文段中获得。
- 当在一个开始的SYN中收到一个CC选项的时候,接收方比较收到的值与为该发送方缓存的CC值。如果接收到的CC比缓存的大,则该SYN是新的,报文段中的任何数据被传递给接收应用进程(服务器)。这个连接被称为半同步。
如果接收的CC比缓存的小,或者接收主机上没有对应这个客户的缓存CC,则执行正常的TCP三次握手过程。 - 为响应一个开始的SYN,带有SYN和ACK的报文段在另一个被称为CCECHO的选项中回显所接收到的CC值。
- 在一个非SYN报文段中的CC值检测和拒绝来自同一个连接的前一个替身的任何重复的报文段。
这种“加速打开”避免了使用三次握手的要求,除非客户或者服务器已经崩溃并重新启动。这样做的代价是服务器必须记住从每个客户接收的最近的CC值。
基于在两个主机之间测量RTT来动态计算TIME_WAIT的延时,可以缩短TIME_WAIT状态。TIME_WAIT时延被设置为8倍的重传超时值RTO。
通过使用这些特征,最小的事务序列是交换三个报文段:
- 由一个主动打开引起的客户到服务器:客户的SYN、客户的数据(请求)、客户的FIN以及客户的CC。当被动的服务器TCP接收到这个报文段的时候,如果客户的 CC比为这个客户缓存的CC要大,则客户的数据被传送给服务器应用程序进行处理。
- 服务器到客户:服务器的SYN、服务器的数据(应答)、服务器的 FIN、对客户的FIN的ACK、服务器的CC以及客户的CC的CCECHO。由于TCP的确认是累积的,这个对客户的FIN的ACK也对客户的SYN、数据及FIN进行了确认。当客户TCP接收到这个报文段,就将其传送给客户应用进程。
- 客户到服务器:对服务器的FIN的ACK,它也确认了服务器的SYN、数据和FIN。客户对它的请求的响应时间为RTT与SPT的和。
许多关于实现这个TCP选项的很好的地方。归纳如下:
- 服务器的SYN和ACK(第2个报文段)必须被迟延,从而允许应答与它一起捎带发送(通常对SYN的ACK是不迟延的)。但它也不能迟延得太多,否则客户将超时并引起重传。
- 请求可以需要多个报文段,但是服务器必须对它们可能失序达到的情况进行处理(通常当数据在SYN之前到达时,该数据被丢弃并产生一个复位。通过使用T/TCP,这些失序的数据将放入队列中处理)。
- API必须使服务器进程用一个单一的操作来发送数据和关闭连接,从而允许第二个报文段中的FIN与应答一起捎带发送(通常应用进程先写应答,从而引起发送一个数据报文段,然后关闭连接,引起发送FIN)。
- 在收到来自服务器的MSS通告之前,客户在第1个报文段中正在发送数据。为避免限制客户的MSS为536,一个给定主机的MSS应该与它的CC值一起缓存。
- 客户在没有接收到来自服务器的窗口通告之前也可以向服务器发送数据。T/TCP建议默认的窗口为4096,并且也为服务器缓存拥塞门限。
- 使用最小3个报文段交换,在每个方向上只能计算一个RTT。加上包括了服务器处理时间的客户测量RTT。这意味着被平滑的RTT及其方差的值也必须为服务器缓存起来。
T/TCP的特征中吸引人的地方在于它对现有协议进行了最小的修改,同时又兼容了现有的实现。它还利用了TCP中现有的工程特征(动态超时和重传、拥塞避免等),而不是迫使应用进程来处理这些问题。
TCP的性能
下图展示了计算以太网理论上最大吞吐量的字段大小:

我们必须计及所有的开销:前同步码、加到确认上的填充字节、循环冗余检验CRC以及分组之间的最小间隔(9.6ms,相当在10Mb/s速率下的12个字节)。
下面这些对TCP性能的实际限制适用于任何的实际情况:
- 不能比最慢的链路运行得更快。
- 不能比最慢的机器的内存运行得更快。
- 不能够比由接收方提供的窗口大小除以往返时间所得结果运行得更快(这就是带宽时延乘积公式,使用窗口大小作为带宽时延乘积,并解出带宽)。
所有这些数字的重要意义就是TCP的最高运行速率的真正上限是由TCP的窗口大小和光速决定的。