39、Internet传输协议之TCP之二(传输层)

1、TCP计时器管理

  • TCP 使用多个计时器(至少从概念上讲是计时器)来完成它的工作。其中最重要的是重传计时器(RTO, Retransmission Timeout )。当TCP 实体发出一个段时,它同时启动一个重传计时器。如果在该计时器超时前该段被确认,则计时器被停止。另一方面,如果在确认到来之前计时器超时,则段被重传(并且该计时器被重新启动)。于是问题就来了:超时间隔应该设为多长?
  • 这个问题在传输层上比诸如802.1 1 那样的数据链路协议要更加困难。在数据链路层,期望的延迟是可以测量的毫秒数,并且是高度可预测的(即方差很小),所以,计时器可以被设置成恰好比期望的确认到达时间稍长即可,如图a所示。由于在数据链路层中确认极少被延迟(因为不存在拥塞),所以,如果在预期的时间内确认没有到来,则往往意味着帧或者确认己经被丢失了。(图a,数据链路层的确认到达时间的概率密度;图b,TCP确认到达时间的概率密度)在这里插入图片描述
  • TCP 面临着截然不同的环境。TCP 确认回到发送端所需时间的概率密度函数看起来更像图b,它更大也更可变。要想确定到达接收方的往返时间非常棘手,即使知道了这段时间,要确定超时间隔也非常困难。如果超时间隔设置得太短,比如说图b中的Tl ,则会发生大量不必要的重传,这么多无用数据包反而堵塞Internet;如果超时间隔被设置得太长(比如T2 ),则一旦数据包丢失之后,由于太长的重传延迟,所以性能会受到影响。而且,确认到达时间分布的均值和方差也会随着拥塞的发生或者解决而在几秒钟时间内迅速地改变。
  • 解决方案是使用一个动态算法,它根据网络性能的连续测量情况,不断地调整超时间隔。TCP 通常采用的算法是CJacobson, 1988 )算法,其工作原理如下所述。对于每一个连接, TCP 维护一个变量SRTT( Smoothed Round-Trip Time,平滑的往返时间〉,它代表到达接收方往返时间的当前最佳估计值。当一个段被发送出去时, TCP 启动一个计时器,该计时器有两个作用,一是看该段被确认需要多长时间: 二是若确认时间太长,则触发重传动作。如果在计时器超时前确认返回,则TCP 测量这次确认所花的时间,比如说R。然后它根据下面的公式更新SRTT:SRTT =αSRTT + (1 一α) R。这里α 是一个平滑因子,它决定了老的RTT 值所占的权重。典型情况下α=7/8 。这类公式是指数加权移动平均(EWMA, Exponentially Weighted Moving Average ),或者丢弃样值中噪声的低通滤波器。
  • 即使有了一个好的SRTT 值,要选择一个合适的重传超时间隔仍然不是一件容易的事情。在最初的实现中, TCP 使用2xRTT,但经验表明常数值太不灵活,当发生变化时它不能够很好地做出反应。尤其是,随机流量的排队模型(即泊松分布〉预测的结果是当负载接近容量时,延迟不仅变大,而且变化也大。这可能导致重传计时器超时而重新传输一个数据包的副本,尽管原先的数据包仍然在网络中传输着。这在高负载的情况下也有可能发生,最糟糕的是将额外的数据包发到网络中。
  • 为了解决这个问题, Jacobson 提议让超时值对往返时间的变化以及平滑的往返时间要变得敏感。这种改变要求跟踪另一个平滑变量, RTTVAR(往返时间变化, Round-Trip TimeVARiation ),即更新为下列公式:RTTVAR =β RTTVAR + (1- β)| SRTT- R |。这就像以前的EWMA ,典型地,β= 3/4。 因此,重传超时值, RTO 为RTO = SRTT + 4 x RTTV AR。这里选择因子4 多少有点随意,但是乘以4 的操作用一个移位操作就可以实现,而且,所有数据包中大于标准方差4 倍以上的不足总数的1%。注意RTTVAR 并不是确切地等于标准方差(真正的它是平均方差),但它实际上足够接近。计算超时值的详细过程,包括变量的初始值设置,可参考盯C2988 。重传计时器的最小值为1 秒,无论估算值是多少。这是为了防止根据测量获得的欺骗性重传值而选择的保守值。
  • 在采集往返时间的样值R 过程中有可能引发的一个问题是,当一个段超时并重新发送以后该怎么办?确认到达时,无法判断该确认是针对第一次传输,还是针对后来的重传。若猜测错误,则会严重影响重传超时值。Phil Karn 发现这个问题很艰难。Kam 是一名业余无线电爱好者,他对在一种极不可靠的无线介质上传输TCP/IP 数据包有浓厚的兴趣。他提出了一个很简单的建议: 不更新任何重传段的估算值。此外,每次连续重传的超时间隔值加倍,直到段能一次通过为止。这个修正算法称为Karn 算法( Karn 和阳tridge, 1987 )。大多数TCP 实现都采用了此算法。
  • 重传计时器并不是TCP 使用的唯一计时器。第二个计时器是持续计时器( persistencetimer)。它的设计意图是为了避免出现以下所述的死锁情况。接收端发送一个窗口大小为0的确认,让发送端等一等。稍后,接收端更新了窗口,但是,携带更新消息的数据包丢失了。现在,发送端和接收端都在等待对方的进一步动作。当持续计时器超时后,发送端给接收端发送一个探询消息。接收端对探询消息的响应是将窗口大小告诉发送端。如果它仍然为0,则重置持续计时器,并开始下一轮循环。如果它非0,则现在可以发送数据了。有些TCP 实现使用了第三个计时器,即保活计时器( keepalive timer )。当一个连接空闲了较长一段时间以后,保活计时器可能超时,从而促使某一端查看另一端是否仍然还在。如果另一端没有响应,则终止连接。这个特性是有争议的,因为它增加了额外的开销,而且有可能由于瞬间的网络分区而终止掉一个本来很正常的连接。每个TCP 连接使用的最后一个计时器被应用于连接终止阶段,即在关闭过程中,当连接处于TMED WAIT 状态时所使用的计时器。它的超时值为两倍于最大数据包生存期,主要用来确保当连接被关闭后,该连接上创建的所有数据包都己完全消失。

2、TCP拥塞控制

  • TCP 的关键功能之一即拥塞控制。当提供给任何网络的负载超过它的处理能力时,拥塞便会产生。Internet 也不例外。当路由器上的队列增长到很大时,网络层检测到拥塞,并试图通过丢弃数据包来管理拥塞。传输层接收到从网络层反馈来的拥塞信息,并减慢它发送到网络的流量速率。在Internet 上, TCP 在控制拥塞以及可靠传输中发挥着主要的作用。这就是为什么TCP 是如此特殊的一个协议。
  • 前面章节中介绍了拥塞控制的一般情况。关键在于如果一个传输协议使用AIMD(加法递增乘法递减)控制法则来响应从网络传来的二进制拥塞信号,那么该传输协议应该收敛到一个公平且有效的带宽分配。TCP 的拥塞控制就是在这个方法的基础上实现的,它使用了一个窗口,并且把丢包当作二进制信号。要做到这一点, TCP 维持一个拥塞窗口(congestion window ),窗口大小是任何时候发送端可以往网络发送的字节数。相应的速率则是窗口大小除以连接的往返时间。TCP 根据AIMD 规则来调整该窗口的大小。
  • 除了维护一个拥塞窗口外,还有一个流量控制窗口,该窗口指出了接收端可以缓冲的字节数。要并发跟踪这两个窗口,可能发送的字节数是两个窗口中较小的那个。因此,有效窗口是发送端认为的应该大小和接收端认为的应该大小两者中的较小者。如果拥塞窗口或流量控制窗口暂时己满,则TCP 将停止发送数据。如果接收端说“发送64 KB数据”,但发送端知道超过32KB 的突发将阻塞网络,它就只发送32KB:另一方面,如果接收端说“发送64 KB数据”,发送端知道高达128 KB的突发通过网络都毫不费力,它会发送要求的全部64 阻。流量控制窗口在前面己经所述过,下面我们只描述拥塞窗口。
  • 现代拥塞控制被添加到TCP 的实现中很大程度上要归功于( Van Jacobson, 1988 )的努力。从1986 年开始,早期Internet 的日益普及导致了第一次出现称为拥塞崩溃( congestion collapse )的问题,由于网络拥塞,在很长一段时间内实际吞吐量急剧下降(即下降幅度超过了100 倍儿Jacobson (和许多其他人)着手了解发生了什么事并试图采取补救措施。
  • Jacobson 实施的高层次修补程序近似于一个AIMD 拥塞窗口。TCP 拥塞控制中最有趣也是最复杂的部分在于如何在不改变任何消息格式的前提下,把这个修补措施部署到现有的TCP 实现中。开始,他观察到丢包可以作为一个合适的拥塞信号。这个信号来得有点晚(因为网络早己拥挤不堪),但它相当值得信赖。毕竟,很难建造一个超负荷时也不丢包的路由器,这是一个不太可能改变的事实。即使可用兆兆位( terabyte )的存储器来缓冲大量的数据包,我们或许会有兆位/秒的网络,它们依然会填满这些存储器。然而,用丢包作为拥塞信号依赖于传输错误相对比较少见的场合。对于诸如802.1 1 的无线链路,传输错误比较常见,这也就是为什么它们在链路层有自己的重传机制。由于无线重传,网络层因传输错误导致的丢包通常被屏蔽在无线网络中。这也是在其他链路上很罕见的情况,因为电线和光纤通常具有较低的误码率。
  • Internet 上的所有TCP 算法都假设丢包是由拥塞和监控器超时所引起的。需要一个良好的重传计时器来准确检测到丢包信号,而且必须以一种及时的方式。我们己经讨论了TCP 重传计时器如何包括了往返时间的均值和方差的估算。为了修复该计时器, Jacobson 工作中很重要的一步是在估算中要包括方差因子。有了良好的重传超时, TCP 发送端可以跟踪发出去的字节,这些就是网络的负载。它只是简单地着眼于传输的序号和确认的序号之间的差异。现在看来我们的任务似乎很容易。我们所需要做的是使用序号和确认号来跟踪拥塞窗口,并使用一个AIMD 规则来调整拥塞窗口的大小。首先要考虑的是数据包发送到网络的方式,即使在很短的时间内也必须匹配网络路径,否则就有可能造成拥塞。例如,考虑一个主机有一个64KB 的拥塞窗口, 它通过一条1 Gbps 的链路连到交换式以太网。如果主机一次发送整个窗口,这样的突发流量或许要穿过前方路径上的某条缓慢的1 Mbps ADSL 线路。这个突发在1 Gbps线路上只需要半毫秒,但在1 Mbps 的线路上则需要半秒,因而阻塞了该条线路,完全扰乱了诸如IP语音这样的传输协议。这种行为告诉我们,设计一个能避免拥塞的协议而不是一个能控制拥塞的协议或许是更好的主意。
  • 然而,事实证明,我们可以使用小的突发数据包来突出我们的优势。下图显示了一个突发数据包的过程。在这个例子中,快速网络上的发送端( 1 Gbps 链路〉突发4 个包给一个慢速网络上的接收端( 1 Mbps 链路),这条链路是路径上的瓶颈或者最慢的部分,我们来看会发生什么情况。最初的4 个包以发送端的发送速率尽快通过链路:因为通过一条慢速链路发送数据包所需要的时间比路由器从快速链路上接收下一个数据包所需要的时间更长,所以在路由器中,它们有的正在被发送, 有的在排队等候输出。但是队列并不长,因为只有少量的数据包被一次发送过来。特别注意在慢速链接上数据包的长度有所增长。同样的数据包,比如说1 KB ,在慢速链路上的发送时间比在快速链路上的发送时间更长,因而相对链路而言,数据包的长度更长。在这里插入图片描述
  • 最终,数据包抵达接收端,在那里它们得到确认。确认的时间反映了数据包穿越慢速链路后到达接收端的时间。相比在快速链路上的原始数据包,这些确认更分散。随着这些确认穿过网络并传回到发送端,它们保留了这个时序。关键的观察是这样的:确认返回到发送端的速率恰好是数据包通过路径上最慢链路时的速率。这正是发送端应该使用的精确发送速率。如果发送端以这个速率往网络注入新的数据包,这些数据包就能以慢速链路允许的速率一样快的速度被转发出去,但它们不会再排队和堵塞沿途上的任何一个路由器。这个时序就是确认时钟( ack clock)。这是TCP 的基本组成部分。通过使用一个确认时钟, TCP 平滑输出流量和避免不必要的路由器队列。
  • 第二个考虑是如果网络拥塞窗口从一个很小的规模开始,那么在快速网络上应用AIMD 规则将需要很长的时间才能达到一个良好的操作点。考虑一个适度的网络路径,它可以支持10Mbps 的传输速率,并且往返时间RTT 为100 毫秒。适当的拥塞窗口是带宽延迟积,即1 MB 或100 个具有1250 字节的数据包。如果拥塞窗口从1 开始(即发送1个具有1250字节的数据包,寒注),每个RTT 递增1 个数据包(就是每个RTT发送一个数据包的意思,寒注),那么需要100 个RTT,或10 秒,连接才能以正确的速率运行。在达到正确的传输速度之前要等待一段漫长的时间。如果从一个更大的初始窗口开始,比如50 个数据包, 那么我们就可以减少启动时间。但是,这个窗口对于慢速或者短程链路又太大了点,如果一次全部用完窗口,它会造成拥堵。
  • 相反,针对这两种情况Jacobson 选择的解决方案是一个线性增长和乘法增长相混合的方法。当建立连接时,发送端用一个很小的值初始化拥塞窗口,最多不超过4 个段:在RFC3390 中描述了该方案的细节,早期的实现采用的初始值是1 ,后根据经验改成了用4 作为初始值。然后发送端发送该初始窗口大小的数据。数据包必须经过一个往返时间才被确认。对于每个重传计时器超时前得到承认的段,发送端的拥塞窗口增加一个段的字节量。此外,随着该段获得确认,现在网络中又少了一段。结果是每一个被确认的段允许发送两个段,每经过一个往返时间拥塞窗口增加一倍。这种算法称为慢速启动( slow start ),但它根本不慢一一它呈指数增长一一相比以前的算法,即一次发送整个流量控制窗口大小的数据,它应该算启动得不快。慢速启动算法如下图所示。在第一次往返时间,发送端把一个数据包注入网络(并且接收端接收到一个数据包)。在接下来的一个往返时间,发送端发出两个数据包,然后在第三个往返时间发送四个。在这里插入图片描述
  • 慢速启动在一定范围内的链接速度和往返时间上都能行之有效,并且使用确认时钟将发送端的传输速率与网络路径相匹配。考查图中确认段从其发送端发送到接收端的方式。当发送端得到一个确认,它就把拥塞窗口大小增加1 ,并立即将两个数据包发送到网络中(其中一个包是新增加的一个:另一个包是替代那个己被确认井离开网络的数据包。任何时候未确认的数据包的数量就是拥塞窗口的大小)。不过,这两个数据包到达接收端的紧随程度不一定非要和它们被发送出来的间隔一样。例如,假设发送端在100 Mbps 的以太网上。每个1250 字节长的数据包需要100 微秒的发送时间。因此两个数据包之间的延迟只有100 微秒。但是如果这些数据包在当前路径的任何地方穿过一条1 Mbps 的ADSL 链路时,形势就发生了变化。现在发送同样的数据报需要10 毫秒。这意味着两个数据包之间的最小时间间隔增长了100 倍。除非数据包必须在此后的某条链路上排队等候,否则它们之间的间距仍然很大。
  • 在图中,这种效果体现在强制数据包以它们之间的最小间距到达接收端。接收端在发送确认时要维持这种间隔,从而发送端也会以同样的间距接收确认。如果网络路径缓慢,确认将抵达得较晚(超过一个RTT 的延迟)。如果网络路径很快,确认会迅速到达(同样,在一个RTT 后)。发送端所要做的只是按照确认时钟的时间来注入新的数据包,这就是慢速启动。
  • 由于慢速启动导致拥塞窗口按指数增长,最终(很快而不是很晚〉它将太多的数据包以太快的速度发到网络。当发生这种情况时,网络中将很快建立起队列。当队列满时,一个或多个包会被丢弃。此后,当确认未能如期返回发送端时, TCP 发送端将超时。有证据表明上图中慢速启动增长太快。经过3 个RTT ,网络中有四个数据包。这四个数据包经过完整的RTT 后到达接收端。也就是说,四个包的拥塞窗口大小就是该连接的正确大小。然而,随着这些数据包被确认,慢速启动持续增长拥塞窗口,在另一个RTT 达到8 个包。这些数据包中只有四个在一个RTT 内到达接收端,无论发送了多少个数据包。也就是说,网络管道己经为满。发送端把额外的数据包放置到网络中将在路由器上产生队列,因为它们被传递到接收器的速度不够快。拥塞和丢包即将发生。
  • 为了保持对慢速启动的控制,发送端为每个连接维持一个称为慢启动阈值( slow start threshold )的阈值。最初,这个值被设置得任意高,可以达到流量控制窗口的大小,因此它不会限制连接速度。TCP 以慢速启动方式不断增加拥塞窗口,直到发生超时,或者拥塞窗口超过该阈值(或接收端的窗口为满〉。每当检测到丢包,比如超时了,慢启动阈值就被设置为当前拥塞窗口的一半,整个过程再重新启动。基本想法是当前的窗口太大,因为是它在过去造成了阻塞,所以现在才检测到超时。以较早时间成功使用的一半窗口也许是拥塞窗口的更好估计值,它更接近路径的容量而不会造成丢失。我们在上图的例子中,当拥塞窗口增长到8 个数据包时,可能造成丢失,而在前一个RTT 中具有4 个数据包的拥塞窗口就是正确值。然后,拥塞窗口被复位到其初始值,恢复慢速启动。
  • 一旦慢速启动超过了阈值, TCP 就从慢速启动切换到线性增加(即加法递增〉。在这种模式下,每个往返时间拥塞窗口只增加一段。像自速启动一样,这通常也是为每一个被确认的段而不是为每一次RTT 实施窗口的增加。回忆一下,拥塞窗口cwnd 和最大段长MSS,一个常见的近似做法是这样的:针对cwnd/MSS 中可能被确认的每个数据包,将cwnd 增加(MSS × MSS) /cwnd。这种增长速度并不需要很快。整个想法是在一个TCP 连接所花费的大量时间中,它的拥塞窗口接近最佳值一一不至于小到降低吞吐量,但也没大到发生拥塞情况。
  • 线性递增如下图所示,这是相同情况下的慢速启动。在每一个RTT 结尾,发送端的拥塞窗口增长到足够它可以向网络注入额外一个数据包。相比慢速启动,线性增长速率要慢得多。这对于小的拥塞窗口差别并不大,就像这里的例子一样,但当随着时间拥塞窗口增长到一定程度时,比如说100 段,差异就大了。我们还可以做些其他事情来提高性能。到目前为止这个方案中的缺陷是等待超时。超时时间相对较长,因为它们必须是保守的。当一个包被丢失后,接收端不能越过它确认,因此确认号将保持不变,发送端因为拥塞窗口为满将无法发送任何新包到网络。这种情况可以持续一段比较长的时期,直到计时器被触发和重传丢失的包。在这个阶段, TCP 再次慢速启动。
  • 发送端有一个快速方法来识别它的包己经被丢失。当丢失数据包的后续数据包到达接收端时,它们触发给发送端返回确认。这些确认段携带着相同的确认号,称为重复确认(duplicate acknowledgement )。发送端每次收到重复确认时,很可能另一个包己经到达接收端,而丢失的那个包仍然没有出现。在这里插入图片描述
  • 因为包可以选择网络中不同的路径,所以它们可能不按发送顺序到达接收端。这样就会触发重复确认,即使没有数据包被丢失。然而,这种情况在Internet 上并不多见。当存在跨越多条路径的包时,收到的数据包通常需要重新排序的不会太多。因此, TCP 有点随意地假设三个重复确认意味着己经丢失一个包(如果是两个的话可能是延迟造成的到达数据包乱序,3个或许更保险,寒注)。丢失包的序号可以从确认号推断出来,它是整个数据序列中紧接着的下一个数据包。因此,这个包可以被立即重传,在其计时器超时前就重新发送出去。这种启发式机制称为快速重传( fast retransmission )。重传后,慢启动阈值被设置为当前拥塞窗口的一半,就像发生了超时一样。重新开始慢启动过程,拥塞窗口被设置成一个包。有了这个窗口大小,如果在一个往返时间内确认了该重传的数据包以及丢包之前己发送的所有数据,则发出一个新的数据包。
  • 下图给出了迄今为止我们己经构建的拥塞算法示意图。此版本的TCP 称为TCP Tahoe,它包括了1988 年发布的4.2BSD TCP Tahoe。这里的最大段长为1 KB 。最初,拥塞窗口为64 KB:但发生超时后,这样的阈值被设置为32 KB,并且拥塞窗口被设置为1 KB,从0 开始传输(从0开始有助于缓解当前的网络拥塞情况,寒注)。拥塞窗口呈指数增长,直到达到阈值(32 KB)。每次到达一个新的确认就增加窗口的大小,而不是连续增加,因而导致窗口的大小表现出离散的阶梯模式。当窗口大小达到阈值后,窗口大小就按线性增长。每个RTT 只增加一段。在这里插入图片描述
  • 第13 轮传输很不幸(它们应该都知道),其中一个包在网络中丢失了。当到达了3 个重复确认时,发送端就检测出丢包行为。此时,发送端重传丢失的包,并将阈值设置为当前窗口的一半(现在是40 KB,因此一半是20KB ),并再次开始慢速启动算法。重新启动时,拥塞窗口大小为一个数据包,在往返时间内,所有以前传输的数据要离开网络并得到承认,而且重传数据包也要得到确认。拥塞窗口的增长与以前慢速启动时一样,直到它达到20KB的新阈值:然后,窗口再次以线性模型增长。窗口持续以线性增长直到通过重复确认检测到另一个丢包或超时(或者接收端的窗口达到上限)。
  • TCP Tahoe (其中包括良好的重传计时器)提供了一个可实际工作的拥塞控制算法,它解决了网络的拥塞崩溃问题。Jacobson 意识到它有可能做得更好。在快速重传时,连接还工作在一个太大的拥塞窗口,但它仍然同样以确认时钟的速率在运行。每次到达另一个重复确认时,可能另一个数据包己经离开网络。使用重复确认来计算网络中的数据包,有可能让一些包离开网络,并继续为每一个额外的重复确认发送一个新的包(意思就是快速重传的当前时间网络可能是拥塞情况,不适合快速重传,重传也可能丢失)。快速恢复( Fast recovery )就是实现这种行为的启发式机制。这是一个临时模式,其目的是保持拥塞窗口上运行确认时钟,该拥塞窗口有一个新阈值或者快速重传时把拥塞窗口值减半。要做到这一点,对重复确认要计数(包括触发快速重传机制的那三个重复确认),直到网络内的数据包数量下降到新阈值。这大概需要半个往返时间。从此时往后,每接收到一个重复确认就发送一个新的数据包(应该是线性递增的意思,寒注)。快速重传之后的一个往返时间后,丢失的包将被确认。在这个时间点,重复确认流将停止,快速恢复模式就此退出。拥塞窗口将被设置到新的慢启动阈值,并开始按线性增长。
  • 这种启发式的结果是TCP 避免了慢速启动,只有第一次启动或者发生超时时才进入真正的慢速启动。当发生多个数据包丢失时,快速重传机制不足以恢复,仍然可能会发生超时。此时,不是反复地进入慢速启动,而是当前连接的拥塞窗口遵循一种锯齿( sawtooth)模式,即加法递增(每个RTT 增加一段)和乘法递减(每个RTT 减半)。这正是我们力求实现的AIMD规则。
  • 这种锯齿行为如下图所示。它由TCP Reno 提供,这是以1990 年发布的4.3BSD Reno 命名的,在那个早期版本中包括了上述机制。TCP Reno 基本上是TCP Tahoe 加上快速恢复机制。经过一个初始的慢速启动,拥塞窗口大小直线攀升,直至通过重复确认检测到丢包。丢失的数据包被重传,并且启用快速恢复机制来维持确认时钟运行,直到重传的数据包被确认。此时,拥塞窗口恢复到新的慢启动阔值,而不是从1 开始。这种行为无限期地继续下去,在连接花费的大部分时间内其拥塞窗口接近于带宽延迟积的最佳值。在这里插入图片描述
  • TCP Reno 及它用来调整拥塞窗口的机制形成了TCP 的拥塞控制,至今己有超过二十年的历史(现在可不止了,今年2019)。在这期间的大多数变化只是以轻微的方式调整这些机制,例如,修改了初始窗口的选择和消除了各种含糊不清的定义。一些改进针对从数据包窗口中两个或两个以上数据包的丢失中恢复过来。例如, TCPNewReno 版本在重传后使用部分提前的确认号来发现并修复另一个丢失的包( Hoe, 1996 ),RFC 3782 对此进行了详细的描述。自20 世纪90年代中期以来,出现了一些TCP 变种,所有这些变种都道循我们描述过的原则,但使用了
    略微不同的控制法则。例如, Linux 使用的TCP 变种称为CUBIC TCP (Ha 等, 2008) ,Windows 则包括了一个称为组合TCP (Compound TCP )的变种( Tan 等, 2006 )。
  • 两个较大的变化对TCP 的实现也有影响。首先, TCP 的许多复杂性来自于从一个重复确认流中推断出已经到达和已经丢失的数据包。累计确认号无法提供这种确切的信息。一个简单的解决方法是使用选择确认( SACK, Selective ACKnowledgement ),该确认列出了3 个已接收的字节范围。有了这个信息,发送端在实现拥塞窗口时可以更直接地确定哪些数据包需要重传,并跟踪那些还在途中的数据包。
  • 当发送端和接收端建立连接时,它们各自在TCP 段的选项字段发送允许SACK ,通知对方它们理解选择确认。一旦在连接上启用SACK,它的工作原理如下图所示。接收端在正常的方式下使用TCP 的确认号字段,作为已收到的最高顺序字节的累积确认。当它接收到乱序的数据包3 时(因为数据包2 丢失了),它发送一个确认段,其中包括针对包1 的(重复)累计确认和包3 的SACK 选项。SACK 选项给出高于累计确认已收到的字节范围。第一个范围是引发重复确认的数据包:接下来的范围,如果存在的话,是旧的数据块。通常采用三个范围。当收到包6 时,要用到两个SACK 的字节范围,来指明包6 和包3~包4已收到,还有额外的到包1 的所有数据包。从它接收到的每个SACK 选项中包含的信息,发送端可以决定重发哪些数据包。在这种情况下,重传数据包2 和包5 将是一个好主意。在这里插入图片描述
  • SACK 是严格意义上的咨询信息。实际上使用重复确认来检测丢包和调整拥塞窗口还是像以前一样。然而,有了SACK, TCP 可以更加容易地从同时丢失多个包的情况下恢复过来,因为TCP 发送端知道哪些数据包尚未收到。SACK 目前己被广泛部署。RFC 2883对SACK 做了描述,而关于TCP 拥塞控制如何使用SACK 则在RFC 3517 中描述。
  • 第二个变化是除了用丢包作为拥塞信号外,显式拥塞通知(ECN, Explicit Congestion Notification )的使用。ECN 是IP 层的机制,主要用来通知主机发生了拥塞。有了它, TCP 接收端可以接收来自IP 的拥塞信号。当发送端和接收端在建立阶段过程中设置了ECE 和CWR 标志位,双方均表示它们能够使用这些标志位后,该TCP 连接就可启用ECN 。如果使用ECN,每个携带TCP 段的数据包在IP 头打上标记,表明它可以携带ECN 信号。支持ECN 的路由器在接近拥塞时就会在携带ECN 标志的数据包上设置拥塞信号,而不是在拥塞发生后丢弃这些数据包。如果到达的任何数据包携带了ECN 拥塞信号,则会告知TCP 接收端。然后接收端使用ECE ( ECN Echo )标志位给TCP 发送端发通知,告知它的数据包经历了拥塞。发送端通过拥塞窗口减少(CWR, Congestion Window Reduced )标志位告诉接收端它已经收到拥塞信号了。
  • TCP 发送端收到这些拥塞通知消息的反应和它根据重复确认检测到丢包的处理方式完全相同。不过,严格来说这种情况更好。拥塞已经被检测出来,而且没有包受到任何方式的损害。ECN 的详细描述见RFC 31680 它要求主机和路由器都必须支持该功能,目前尚未在Internet 上广泛应用。有关TCP 实现中拥塞控制行为的详细信息,请参见RFC 5681 。

3、TCP未来

  • 作为Internet 的主力军, TCP 已经被用于许多应用,并随着时间的推移做了许多扩展以便在广泛的网络上性能表现良好。许多实际部署的版本在实现上跟我们所描述的经典算法略微有所不同,尤其是拥塞控制和针对攻击的鲁棒性方面。很有可能TCP 将伴随着Internet 的发展而持续地改进着。这里,我们会特别提到两个问题。
  • 第一个问题是TCP 没有为所有应用程序提供所需的传输语义。例如,某些应用程序需要保留其发送的消息或记录边界。其他的应用程序要处理一组相关的对话,如Web 浏览器从同一台服务器传输几个对象。还有其他应用程序希望更好地控制它们使用的网络路径。但是TCP 的标准套接字接口并不满足这些需求。从本质上讲,应用程序承担着解决任何TCP 没有解决的问题的负担。这导致了研究者们提出了具有稍微不同接口形式的新协议的建议,其中两个比较著名的例子是由RFC 4960 定义的流控制传输协议( SCTP, Stream Control Transmission Protocol )和结构化流传输( SST, Structured Stream Transport)(Ford, 2007 )。
  • 第二个问题是拥塞控制。你或许期望经过我们的审议以及随着时间推移而开发出来的一系列机制已经解决了这个问题。事实并非如此。我们介绍的TCP 拥塞控制的形式虽然已经被广泛使用,但这是基于丢包作为拥塞的信号。当( Padhye 等, 1998 )根据锯齿模式为TCP 吞吐量建立模型时,他们发现随着速度的加快丢包率呈现急剧下降的趋势。对于往返时间为100 毫秒,数据包长度为1500 字节,要达到1 Gbps 的吞吐量,大约每隔10 分钟要丢一个包。这种情况下的丢包率大约是2 × 10-8,显然这个数字令人难以置信的小。把丢包率作为一个良好拥塞信号实在是太罕见了,任何其他来源的损失〈例如,数据包的传输误码率10-7 )可以很容易地发挥主宰作用,由此真正限制了吞吐量(说的是丢包率可能并不是拥塞主要造成的,寒注)。这种关系在过去不是一个问题,但网络速度越来越快,导致许多人重写拥塞控制。一种可能性是干脆使用另一种拥塞控制,不把丢包当作拥塞信号。在前面章节中给出了几个例子。拥塞信号可以是往返时间,当网络变得拥塞时往返时间会增长,如FAST TCP 所用的那样( Wei 等, 2006 )。其他方法也是可能的,时间会告诉我们什么是最好的控制方式。

猜你喜欢

转载自blog.csdn.net/ao__ao/article/details/88593062