TCP可靠传输详解

 

TCP提供了可靠的传输服务,这是经过下列方式提供的:
  • 分块发送:应用数据被分割成TCP认为最适合发送的数据块。由TCP传递给IP的信息单位称为报文段或段(segment)
  • 定时确认重传:当TCP发出一个段后,它启动一个定时器,等待目的端确认收到这个报文段。若是不能及时收到一个确认,将重发这个报文段。
  • 当TCP收到发自TCP链接另外一端的数据,它将发送一个确认。这个确认不是当即发送,一般将推迟几分之一秒
  • 数据校验:TCP将保持它首部和数据的检验和。这是一个端到端的检验和,目的是检测数据在传输过程当中的任何变化。若是收到段的检验和有差错,TCP将丢弃这个报文段和不确认收到此报文段(但愿发端超时并重发)。
  • 正确排序:因为IP数据报的到达可能会失序,所以TCP报文段的到达也可能会失序。若是必要,TCP将对收到的数据进行从新排序,将收到的数据以正确的顺序交给应用层。
  • 重复丢弃:IP数据报会发生重复,TCP的接收端必须丢弃重复的数据。
  • 流量控制:TCP链接的每一方都有固定大小的缓冲空间。TCP的接收端只容许另外一端发送接收端缓冲区所能接纳的数据。这将防止较快主机导致较慢主机的缓冲区溢出。

1、可靠传输的原理

因为网络环境的复杂性,网络上的数据可能丢失、发生错误,于是可靠传输是网络传输中的最基本的问题。只要涉及到网络就逃避不开这个问题。html

 

1.可靠数据传输协议

1.1  彻底可信信道上的可靠传输

若是信道彻底可靠,那么可靠传输就不成问题了,此时的可靠传输很是简单。发送方只须要将数据放到信道上它就能够可靠的到达接收方,并由接收方接收。可是这种信道是彻底理想化的,不存在的。算法

 

1.2  会出现比特错误的信道上的可靠传输

更现实一点的信道是会发生比特错误的,假设如今须要在除了会出现比特错误以外,其它的特性和彻底可靠信道同样的信道上进行可靠传输.缓存

为了实现该信道上的可靠传输:网络

  • 接收方:须要确认信息是否就是发送方所发送的,而且须要反馈是否有错误给发送方
  • 发送方:须要在发送的信息中添加额外信息以使得接收方能够对接收到的信息是否有错误进行判断,而且须要接收接收方的反馈,若是有错误发生就要进行重传
于是在这种信道上进行传输须要三种功能:
  • 差错检测:发送方提供额外信息供接收方进行校验,接收方进行校验以判断是否有错误发生。网络协议通常采用校验和来完成该任务
  • 接收方反馈:接收方须要将是否有错误发生的信息反馈给发送方,这就是网络协议中最多见的ACK(确认)/NAK(否认的确认)机制
  • 发送方重传:在出现错误时,发送方须要重传出错的分组。重传也是网络协议中极常见的机制。

上述机制还有问题,它没有考虑接收方的反馈出现比特错误 即 反馈受损 的情形。采用上述机制,在反馈受损时,发送方能够了解到这个反馈信息出现了错误,可是它没法知道反馈的是什么样的信息,所以也就没法知道本身该怎么应对。并发

这能够有两种解决办法:socket

  • 发送方提供足够多的信息,使得接收方不只能够检测比特错误,并且能够恢复比特错误。这在仅会发生比特错误的信道上是理论可行的,代价是须要大量额外的信息。
  • 若是收到了受损的反馈,则都认为是出现了错误,就进行重传。可是这时就可能引入冗余的分组,由于被重传的分组可能已经正确的被接收了。

网络协议中普遍采用的是第2种解决方案,冗余分组能够经过一种简单的机制来解决,这就是分组序列号。被发送的每一个分组都有一个序列号,接收方只须要检测该序列号就能够知道分组是不是冗余的。函数

 

在引入序列号后,该机制已经能够在这种信道上工做了。不过它还能够作一点变化,有些网络协议中并不会产生否认的确认(即报告发送方出现了错误),它采用的是继续为 已经为之发送过ACK的最后一个正确接收的分组 发送ACK。当发送方收到冗余的ACK时就知道跟在被冗余ACK确认的分组以后的分组没有被正确接收,这就达到了NAK所要的效果。性能

 

1.3  会出现比特错误而且会丢包的信道上的可靠传输

这种信道是更常见的信道。比特错误的问题已经被校验和、序号、ACK和重传解决了。如今须要引入新的机制来解决丢包的问题。

丢包问题的解决很简单,对于发送方来讲只要在必定时内没有收到分组的ACK就认为分组丢失了,就进行重传便可。固然这可能引入冗余的分组,可是序列号是能够解决冗余分组的问题,于是惟一须要肯定的就是所谓的必定的时间内的时间长度。很显然时间长度至少要为“往返时延+分组处理时间”。因为网络环境的复杂性,该值的估算每每也是每一个网络协议中的很重要的一部分。spa

 

2 流水线/可靠数据传输协议

上述可靠传输协议是一个中止等待协议,它的性能不好。任意时刻信道内只有一个分组(或者数据分组或者确认分组)。这就极大的浪费了带宽。发送方的利用率是:
 (分组长度/传输速率) /  (RTT +分组长度/传输速率)
传输速率即信道的速率。这个利用率是很低的。解决利用率低的问题的很完美的一个现实参考模型就是工厂流水线。为了模拟流水线,对可靠传输协议的一个修改是:不采用中止等待协议,而是容许发送方发送多个分组而无需等待确认。具体的修改包括:
  • 增长分组序号范围,由于每一个传输的分组须要有一个惟一的序号,并且同时存在多个未确认的分组
  • 协议的发送方和接收方须要缓存多个分组,发送方至少须要缓存已发送但未确认的分组

所需分组序号的范围和对缓存分组的要求取决于如何解决分组丢失、出错或者超时的问题。.net

           流水线的差错恢复有两种手段:回退N步(GBN)和  选择重传。

2.1  GBN

该协议中,流水线中未被确认的分组数目不能超过N。 GBN协议中发送方两个重要的序号为:
  • 基序号(send_base):最先未被确认的序号
  • 下一个序号(nextseqnum):最小未使用的序号
发送方看到的序号被分为几部分:
  • [0, send_base - 1]:已发送而且收到确认了的序列号
  • [send_base,  nextseqnum - 1]:已发送但未收到确认了的序列号
  • [nextseqnum, send_base + N - 1]:可被用于发送新分组的序列号
  • 大于等于send_base + N:不能使用的序列号
本协议中,已发送但未被确认的分组数目不能超过N。所以N能够看作是从第一个已发送但未被确认的序号(就是基序号)开始的长为N的序号窗口,也称为发送窗口。随着分组被确认,该窗口逐渐滑动,send_base增长,可是其大小不变,所以该协议也称为滑动窗口协议。假设序号空间总大小为X,全部的序号计算都要对X取模。

1)在GBN协议中,发送方须要处理如下事件:

  • 上层调用进行发送:当发送数据时,首先判断发送窗口是否已满,只有不满时才会启动发送,若是满了则不能发送或者缓存或者通知调用者,这取决于实现。(判满 )
  • 收到ACK:若是收到了分组n的ACK,而且分组n的序号在[send_base,  nextseqnum - 1]之间,则更新send_base。GBN协议采用累积确认,含义是若是发送方接收到了对分组n的确认,则代表分组n以前的全部分组都已经被接收。
  • 超时事件:GBN协议名字来自该协议对分组丢失或超时事件的处理。若是分组丢失或者指定的时间内仍未收到对已发送可是未收到确认的分组的确认,则发送方重传[send_base, nextseqnum-1]之间的全部分组。实现中能够只为序号为send_base的分组启动一个定时器,若是send_base被更新就重启send_base相关的定时器,若是没有已经被发送但未被确认的分组,则关闭定时器。

2)GBN协议中,接收方的行为:

       接收方接收到分组n时,若是n是按序接收的,即分组n以前的全部分组都已经到达,则发送一个对分组n的确认,并将分组提交给上层。全部其它状况,接收方都丢弃分组,并重传对最后一个按序接收的分组的确认。这种设计简化了接收方接收缓存的设计,同时被丢弃的分组迟早都会被重传,于是可靠性也是有保证的。
       GBN协议的接收方只维护了一个指望收到的下一个序号的信息,并保存在expectedseqnum中,在expectedseqnum以前的全部分组都已经被正确接收并提交给了上层。接收方具体的行为以下:
  • 若是收到的分组的序号与expectedseqnum相同,则将分组提交给上层,并更新它的值为下一个指望收到的序号。
  • 若是收到的分组的序号大于expectedseqnum,就丢弃分组。
  • 若是收到的分组的序号小于expectedseqnum,就重传对最后一个按序接收的分组的确认。因为GBN是累积确认的,所以该确承认以确保发送窗口能够向前移动。

2.2  选择重传

GBN协议存在缺陷,在超时或者分组丢失时它会重传全部的在[send_base, nextseqnum-1]之间的分组,而接收方也会丢弃非按序到达的分组,这也浪费了带宽。

1)发送方

选择重传协议经过让发送方仅重传那些它怀疑在发送方出错的分组来避免没必要要的重传。

该协议中发送方看到的序号空间与GBN协议彻底相同,惟一不一样的在于序号空间[send_base,  nextseqnum - 1]中的分组包含了一些已经被接收方确认了的分组。

发送行为:
  • 上层调用进行发送:当发送数据时,首先判断发送窗口是否已满,只有不满时才会启动发送,若是满了则不能发送或者缓存或者通知调用者,这取决于实现,
  • 收到ACK:若是收到分组n的ACK,而且n的序号在[send_base,  nextseqnum - 1]之间,则将分组n状态更新为已确认,若是n的序号等于send_base,则更新send_base到下一个已发送但未被确认的分组的序号处。
  • 超时事件:该协议使用选择重传,所以每一个分组都有一个定时器,若是某个分组的定时器到期了就重传该分组。

2)接收方

该协议中,接收方也须要维护一个长度为N的接收缓存,而且须要维护一个变量rcv_base,它表示接收方指望收到的下一个分组的序号。接收方看到序号空间被划分为:
  • [0, rcv_base - 1]:已经正确接收而且被确认了的序号空间
  • [rcv_base, rcv_base + N -1]:指望接收的序号空间
  • 大于rcv_base + N :不可用的序号空间
接收行为:
  • 收到了序号在[rcv_base – N , rcv_base -1]之间的分组:产生一个ACK,这是必须的,由于发送方重传了该分组就说明它没有收到对它的ACK,若是不发送ACK,发送方的窗口将不会移动。假设N=5,考虑如下场景
    • 发送方发送了序号为5,6,7,8,9的分组,所以send_base=5
    • 接收方接收了这些分组而且为全部分组都发送了ACK,所以rcv_base=10
    • 全部ACK都丢失
    • 发送方重传分组5
    • 接收方必须发送ACK,不然发送方窗口将不会被更新
  • 收到了序号在[rcv_base, rcv_base + N -1]之间的分组:发送ACK给发送方。若是该分组之前未接收,则缓存它;若是该分组的序号等于rcv_base,则将从rcv_base开始的序号连续的被缓存的接收到的分组提交给上层,同时更新rcv_base为有序接收到的最后一个分组的序号的下一个序号。
  • 接收到其它分组:丢弃分组

注意到接收方必须能够处理序号范围在[rcv_base, rcv_base + N -1]和[rcv_base – N , rcv_base -1]之间的分组,这两个区间的总长度为2N。所以这就意味着序号空间至少要有2N个可用序号值,即序号空间的大小至少要为2N,不然接收方就没法区分一个分组是新的分组仍是一个重传。

 

2、TCP数据传输

当TCP链接创建以后,应用程序便可使用该链接进行数据收发。应用程序将数据提交给TCP,TCP将数据放入本身的缓存,而且在其认为合适的时候将数据发送出去。在TCP中,数据会被当作字节流并按照MSS的大小进行分段,而后加上TCP头部并提交给网络层。以后数据就会被网络层提交给目地主机,目地主机的IP层会将分组提交给TCP,TCP根据报文段的头部信息找到相应的socket,并将报文段提交给该socket,socket是和应用关联的,也就提交给了应用。

 

1.TCP的可靠传输

       IP提供的服务是尽力交付的服务,也是不可靠的服务。可是TCP在IP之上提供了可靠度传输服务。TCP采用了流水线下的可靠数据传输协议,可是在差错恢复时,并无简单的采起GBN协议或者选择重传协议,而是将两者结合了起来。

 

      TCP采用了累积确认的方式,这相似于GBN,即若是TCP发送了对某个序号N的确认,则代表在N以前的全部字节流都已经被正确接收。可是另外一方面,TCP又不会像GBN协议那样简单丢弃失序到达的报文段,而是会将它们缓存起来,可是这些被缓存的报文段不会逐个被确认。当发生超时时,TCP只会重传发生超时的那一个报文段。

      TCP还容许接收方选择性的确认失序到达的分组,而不是累积的对最后一个确认最后一个正确到达的分组,将它与TCP所采起的选择重传结合起来看就很想选择重传协议的工做机制。所以说TCP的差错恢复结合了GBN和选择重传。

 

      选择重传中每一个报文段都有本身的超时值,TCP采用了RFC2988建议的机制用一个单必定时器来完成该功能。RFC2988定义的原则:

  1. 发送TCP分段时,若是尚未重传定时器开启,那么开启它。
  2. 发送TCP分段时,若是已经有重传定时器开启,再也不开启它。
  3. 收到一个非冗余ACK时,若是有数据在传输中,从新开启重传定时器。
  4. 收到一个非冗余ACK时,若是没有数据在传输中,则关闭重传定时器。

1.1  基本工做过程

发送,ACK,重传共同保证了TCP的可靠传输,其基本工做过程(考虑累积确认的情形)以下:

1.2  发送分组

TCP会为发送的每个分组分配一个惟一的序号,该序号和ISN以及该报文段在字节流中的位置有关。序号被填入TCP头部的序号字段。若是重传定时器尚未运行,则会启动重传定时器。

1.3 接收到ACK

因为是累积确认的,所以若是收到的ACK是合法的,便是对已发送但未被确认的报文段的确认,则更新send_base,而且若是还有未被确认的已发送的报文段,则重启重传定时器。

1.4 超时

重传引发超时的报文段,并重启定时器。TCP的重传不必定是重传引发超时的报文段自己,TCP可能从新进行分组而后重传,惟一被保证的是全部数据都会被传输。

1.5 产生ACK

每一个TCP报文段的TCP头部都固定包含了ACK域,若是在传输中,为了确认一个报文段而单独发送一个ACK,则该ACK就是一个数据部分长度为0的特殊TCP报文段,若是这样的分段太多,网络的利用率就会降低。为此,TCP采起了延迟确认的机制。其工做过程:
  • 若是收到的报文段的序号等于rcv_base,而且全部在rvc_base以前的报文段的确认都已经被发送,则只更新rcv_base,可是延迟该报文段的ACK的发送,最多延迟500ms。延迟的ACK可能会在接收端有数据要发送给发送端时被发送或者在接收端有多个ACK须要被发送给发送端时被发送。
  • 若是收到的报文段的序号等于rcv_base,而且有延迟的ACK待发送,则更新rcv_base,并发送累积的ACK以确认这两个按序报文段
  • 若是收到的报文段的序号大于rcv_base,则发送冗余的ACK,即重传对已经确认过的最后一个按序到达的报文段的ACK

2.往返时延的估算与超时

TCP协议定义了RTT来表明一个TCP分段的往返时间。然而因为IP网络是尽力而为的,而且路由是动态的,且路由器可能缓存或者丢弃IP数据报,所以一个TCP链接的RTT是动态变化的,于是也须要动态测量。样本RTT(SampleRTT)是报文段被发出到报文段的确认被收到的时间间隔。TCP不会为每个发动的报文段测量一个SampleRTT,而是仅为已发送可是未被确认的分组测量SampleRTT。这样作是为了产生一个近似于RTT的SampleRTT。TCP不会为重传的报文段测量SampleRTT。
获得多个SampleRTT后,TCP会尝试使用这些信息来尽量获得一个较为准确的RTT,为此TCP采用了常常被采用的收到即便用一个滤波器来对多个SampleRTT进行计算。TCP使用以下的滤波器来计算一个EstimateRTT:
 EstimateRTT= (1- α) * EstimateRTT  +α * SampleRTT
RFC2988给出的α参考值为1/8。EstimateRTT 是一个平滑后的RTT。
除此以外,TCP还将RTT的变化率也应该考虑在内,若是变化率过大,则经过以变化率为自变量的函数为主计算RTT(若是陡然增大,则取值为比较大的正数,若是陡然减少,则取值为比较小的负数,而后和平均值加权求和),反之若是变化率很小,则取测量平均值。TCP计算了一个DevRTT。它用于估量SampleRTT偏离EstimateRTT的程度。其公式为:
 DevRTT= (1-β)* DevRTT +  β* |SampleRTT - EstimateRTT |
β的参考值为1/4。

以后重传定时器的值会被设置为EstimateRTT + 4 * DevRTT

3.倍数增长的重传间隔

       在发生超时重传时,TCP不是以固定的时间间隔来重传的,而是会再每次重传时都将下一次重传的间隔设置为上次重传间隔的2倍,所以重传间隔是倍数增长的。直到收到确认或者完全失败。因为正常发送报文段时,重传定时器的超时值为EstimateRTT + 4 * DevRTT,所以第一重传时会将下一次的超时时间设置为2倍的该值,依次类推。

4.快速重传

      倍数增长的重传间隔会增大端到端的时延,使得发送端可能不得不等待很长时间才能重传报文段。冗余ACK使得TCP能够获得分组丢失的线索。TCP基于冗余ACK提供了一种快速重传机制。其原理是:若是收到了对相同数据的三个冗余的ACK,发送端就认为跟在这个被确认了三次的报文段以后的报文段丢失了,所以重传它,而不是等待它的超时定时器到期。这就是快速重传。

5.流量控制

      在TCP中,链接双都为该链接设置了接收缓存,当报文段被链接的一端接收时,它会进入该接收缓存,被接收的数据并不必定当即被提交给应用程序。由于应用可能因为各类而没能及时读取缓存中的数据。若是发送方发送的数据太快,而应用没有及时读取被缓存的数据,缓存就会变满,此时为了防止缓存溢出,就要丢弃报文段,显然丢弃已经正确接收的报文段是对网络资源的浪费。为了解决该问题,TCP须要提供一种机制来防止接收缓存溢出。
      TCP提供了流量控制功能,来防止发送方发送过快而致使接收方缓存溢出的情形出现。这是经过让接收方通告一个接收窗口大小来实现的。接收窗口的大小包含在TCP头部的窗口大小字段中。其工做原理为:
接收方经过窗口大小通告本地能够接收的报文段的总大小。发送方将根据该信息来判断本身能够发送多少数据。发送方保证本身发送的未被确认的报文段的总大小不超过接收方通告的窗口大小。对应到咱们所描述的GBN和选择重传协议中,就是发送方会用接收方通告的窗口大小更新本地的窗口大小N的值。一个可视化的描述以下图:

 

可是TCP链接的一端可能通告一个大小为0的窗口,这时候接收到对端通告大小为0的窗口的一端并不会中止发送,而是会启动一个定时器来发送窗口探测报文段,该报文段只包含一个字节,该报文段会被接收方确认,该定时器会一直重启自身来发送窗口探测包直到对端通告了一个大小不为0的窗口为止。定时器的超时值会逐渐增大到一个最大值,而后固定以该值重发窗口探测包。

2.SWS(糊涂窗口综合症)

糊涂窗口综合症是指在发送端应用进程产生数据很慢、或接收端应用进程处理接收缓冲区数据很慢,或两者都存在时,经过TCP链接传输的报文段会很小,这会致使有效载荷很小。极端状况下,有效载荷可能只有1个字节;而传输开销有40字节(20字节的IP头+20字节的TCP头) 这种现象就叫糊涂窗口综合症。

1.发送端引发的SWS

若是TCP发送端的应用是产生数据很慢的应用程序(好比telnet),它可能一次只产生一个字节。这种应用程序一次只往TCP提交一个字节的数据,若是没有特殊的处理,这就会致使TCP每次都产生一个只有一个有效载荷的报文段。最终致使网络的有效利用率很是低。解决办法是防止TCP发送太小的报文段,若是应用提交的数据较短,就等待足够的数据来组成一个较大的报文段再发送,为了防止长时间等待致使时延过大,能够加入一个等待时间限制,若是时间到期还没等到足够的数据就直接发送再也不等待。Nagle算法就是这样的一种算法。

2.接收端引发的SWS

若是TCP接收端的应用处理数据的速度很慢,一次只从TCP缓存取走很小数量的数据,好比一个字节,而发送方发送的速度较快,这就会致使接收方的缓存被填满,而后接收方每次在应用取走一个字节的数据后都通告一个大小为1的窗口,这就限制发送方每次只能发送包含一个字节的有效载荷的报文段。
对于这种糊涂窗口综合症,即应用程序消耗数据比到达的慢,有两种建议的解决方法:
  1. Clark解决方法 Clark解决方法是只要有数据到达就发送确认,但通告的窗口大小为零,这个过程持续到缓存空间已能放入具备最大长度的报文段或者缓存空间的一半已经空了。
  2. 延迟确认 第二个解决方法是延迟一段时间后再发送确认。这时接收方不当即确认收到的报文段。接收方在确认收到的报文段以前一直等待,直到入缓存有足够的空间为止。该方法阻止了发送端滑动其窗口,当发送端发送完其数据后,它就停下来了。这样就防止了这种症状。延迟的确认还减小了通讯量。接收端不须要确认每个报文段。但它有可能使发送端重传其未被确认的报文段。能够给延迟的确认加一个时间限制来下降该方法缺点的影响。

3.Nagle算法

Nagle算法的核心思想是任意时刻,最多只能有一个未被确认的小段。 所谓“小段”,指的是小于MSS尺寸的数据块。Nagle算法的规则:
  1. 若是包长度达到MSS,则容许发送;
  2. 若是该包含有FIN,则容许发送;
  3. 设置了TCP_NODELAY选项,则容许发送;
  4. 未设置TCP_CORK选项时,若全部发出去的小数据包(包长度小于MSS)均被确认,则容许发送; 
  5. 上述条件都未知足,但发生了超时(通常为200ms),则当即发送。
Nagle算法在任意时刻只容许存在一个未被确认的报文段,但他它并不关心报文段的大小,所以它事实上就是一个扩展的中止等待协议,只不过它是基于报文段的而不是基于字节的。Nagle算法彻底由TCP协议的ACK机制决定,所以也有一些缺点,好比若是对端ACK回复很快的话,Nagle事实上不会拼接太多的数据包,虽然避免了网络拥塞,网络整体的利用率依然很低。 
在某些时刻也可能会须要关闭该算法,尤为是交互式的TCP应用,由于这种应用指望及时收到响应。这能够经过打开TCP_NODELAY选项来实现。

4.TCP_CORK 选项

设置该选项后,内核会尽力把小数据包拼接成一个大的数据包(一个MTU)再发送出去,固然若必定时间后(通常为200ms,该值尚待确认),内核仍然没有组合成一个MTU时也必须发送现有的数据。

5.Nagle算法与CORK算法区别

Nagle算法和CORK算法很是相似,可是它们也有区别:

 

  1. 它们的目地不一样。Nagle算法主要避免网络由于有太多的小报文段而拥塞,而CORK算法则是为了提升网络的利用率,使得整体上协议头占用的比例尽量的小。
  2. 用户经过TCP_NODELAY来启用或禁用Nagle算法而经过TCP_CORK来启用或禁用CORK算法。
  3. Nagle算法关心的是网络拥塞问题,只要有ACK回来则发包,而CORK算法关心的是报文段大小,在先后数据发送间隔很短的前提下,即便你是分散发送多个小数据包,你也能够经过使能CORK算法将这些内容拼接在一个包内,若是此时用Nagle算法的话,则可能作不到这一点。

 

3.拥塞控制

当网络拥塞时数据报不能及时被转,在分组转发网络中,数据报就会被排队,甚至出现丢包所以说网络拥塞会带来网络销:
  • 引入大的排队时延
  • 当数据报被丢失时发送方必须重传,所以引入了重传开销
  • 当数据报被丢失时,丢失路由器的上游路由器作的工做都变成了无用功
所以必须采起技术来尽量避免拥塞。因为IP层不提供网络是否拥塞的信息,于是TCP必须本身来判断网络是否出现了拥塞。
TCP将丢包(多是超时也多是收到了三个冗余的ACK)看作是网络拥塞的线索,将RTT增长看作是网络拥塞程度加剧的线索。
TCP让链接双方根据本身所判断的网络拥塞的程度来限制其发往网络的流量。TCP在链接的每一端都增长了一个变量cong_win,它表示拥塞窗口,用于限制一端能够向网络发送的数据。TCP链接的每一端都保证它所已经发送的未被确认的报文段的总大小不会超过拥塞窗口和对方通告的窗口大小中的较小的那一个。
TCP经过ACK到达的状况(便是否到达,到达的速率)来调整拥塞窗口的大小。

1.慢启动

在创建TCP链接时,拥塞窗口被初始化为 min (4*SMSS, max (2*SMSS, 4380 bytes)) 。可是TCP不是以线性的方式增大拥塞窗口,而是以指数的方式增长的,即:

 

  1. 初始设置cwnd=1个 min (4*SMSS, max (2*SMSS, 4380 bytes)) ,发送一个报文,在RFC5861中有更新,可是整体就是一个较小的值(SMSS:SENDER MAXIMUM SEGMENT SIZE : The SMSS is the size of the largest segment that the sender can transmit)。(对于SCTP,cwnd初始值为min(4*MTU)。
  2. 收到对该报文的ack,则cwnd被设置为2个MSS,能够发送两个报文
  3. 收到对2个报文的确认后,cwnd更新为4个MSS,能够发送四个报文,一依次类推。

 

该过程会一直持续直到遇到一个丢包事件为止(或者等于ssthresh时,ssthresh事实上是用于区分是进行的拥塞避免仍是慢启动,初始化时它被设置为65536),事实上该算法至关于每收到对一个MSS的确认就将cwnd增大一个MSS(cwnd决定了当前能够发送cwnd/MSS个报文,当这个cwnd/MSS个报文段都被确认后,就将cwdn增大了cwnd/MSS × MSS即cwnd)。发送方取拥塞窗口与对端通告窗口中的最小值做为发送上限。因为初始的拥塞窗口很小的值,由于该启动方式被称为慢启动。

可是对于SCTP,它只有在知足三个条件时才增大cwnd,min(上次发送的全部数据的大小,MTU):

 

  1. cwnd已经被用完
  2. 累积确认被更新了
  3. 当前不处于快速恢复模式

 

2.拥塞避免(加性增,乘性减)

当遇到一个丢包事件时,TCP会将其拥塞窗口下降为原来值的一半,同时将ssthresh设置为 max (FlightSize / 2, 2*SMSS) (FlightSize:The amount of data that has been sent but not yet acknowledged.)(对于SCTP为:max(cwnd/2, 4*MTU),整体而言,SCTP和TCP的拥塞控制、避免算法是一致的,用MTU替换掉MSS便可。另外SCTP只在有大于等于cwnd的数据正在被发送时(onflight)才更新cwnd) 。其目的是经过下降发送速率来缓解、避免拥塞。可是拥塞窗口大小至少为1个MMS。
在非启动期间,当TCP探测到没有拥塞时,即当链接的一端收到了对它已经发送但未被确认的报文段的确认时,它就会增大拥塞窗口,增大的方式是每收到一个ACK将拥塞窗口大小增大MSS×MSS/cwnd,所以在该算法下,通过一个RTT,cwnd最多增大一个MSS。
所以TCP的拥塞控制方式又称为加性增,乘性减。拥塞窗口的增长受惠的只是本身,而拥塞窗口减小受益的是你们,当出现拥塞时,经过乘性减虽然损害了本身,可是可让更多的其它网络参与者受益,这也证明TCP拥塞控制中的公平性的核心所在。

3.对超时事件的处理

虽然超时和收到三个冗余ACK(SCTP中不存在三个冗余ACK,对应的事件是三个SACK都不包含都某个报文段的确认,则认为该报文段丢失须要重传)都被认为是丢包事件,可是TCP在两者的处理上并不全相同。当收到三个冗余ACK时,TCP的处理就是“加性增,乘性减”。可是若是是超时事件,则TCP会更新ssthresh的值为max (FlightSize / 2, 2*SMSS)(对于SCTP,max(cwnd/2, 4*MTU)),而后进入“慢启动”过程,即将拥塞窗口设置为一个MSS,而后指数增长拥塞窗口大小。此时“慢启动”会持续到遇到一个丢包事件或者拥塞窗口被增大到了ssthresh,若是是增大到了ssthresh则进入“拥塞避免”的模式,即开始加性增。
所以对于丢包事件来讲,只要发生了丢包,则ssthresh都会更新max (FlightSize / 2, 2*SMSS)(对于SCTP,max(cwnd/2, 4*MTU))。若是是超时,cwnd被设置为一个MSS(对于SCTP,1个MTU );若是是冗余ACK,则cwnd被更新为更新后的ssthresh。随后cwnd的更新方式取决于它和ssthresh之间的大小关系,若是cwdn小于或等于ssthresh,则就是在执行慢启动,不然就是在执行拥塞避免。

对超时时间和三个冗余ACK处理方式上存在区别的缘由在于,收到冗余ACK代表了网络时能够交付报文段的,是可用的,而超时则就是明确的丢包。而收到冗余ACK至少代表网络仍是可用的,只是出现了丢包,事实上,在出现三个丢包的时候,采用的是快速恢复+快速重传+拥塞避免。

 

4. 快速恢复

快速恢复通常和快速重传一块儿实现,其算法为:
  1. 当收到第3个重复的ACK时(对于SCTP,有三个SACK都不包含某个报文的确认时。具体规则:1.只有报文TSN小于当前SACK中新被确认的最大TSN的被丢失的报文的丢失计数才会增长,2.若是已经处于快速重传模式,而且当前的SACK会更新累积确认点,则全部丢失的报文的丢失计数都会增长(这一点将保证报文会被尽快快速重传,从而使得尽快退出快速重传模式)。),把ssthresh设置为max (FlightSize / 2, 2*SMSS)(对于SCTP,max(cwnd/2, 4*MTU)),把cwnd设置为ssthresh的值加3个SMSS(对于SCTP,不增长)。而后重传丢失的报文段。由于收到3个重复的ACK代表有三个报文已经离开网络到达了接收断,被且被接收端给接收了。(同时,对于SCTP,还会将当前已经发出的最大的报文序号(TSN)做为退出快速恢复的序列号)
  2. 再收到重复的ACK时,cwnd增长一个MSS。
  3. 当收到确认新数据包的ACK时,把cwnd设置为第一步中的ssthresh的值。此时就从新进入到了第一步丢包时本应进入的拥塞避免。(对于SCTP,若是收到的SACK的累积确认确认了步骤1中的退出快速恢复的序列号,则退出快速恢复)

       快速恢复意在经过快速重传丢失的报文,使得接收端能够将累积确认的最后一个报文和乱序到达的报文之间的“间隙”填充起来,从而尽快进行新的确认。在这个过程当中,每当接收断收到一个新的乱序的报文,发送端就将本身的cwnd增大一个MSS,使得发送端能够尽快填充接收端的“间隙”,直到累积确认的报文段以后有新的连续的报文段被接收端接收到了,这个时候接收端会更新一个新的ACK,发送端在收到该ACK后就退出快速恢复并进入拥塞避免。

        须要注意的是不管是拥塞避免仍是慢启动,SCTP规定,若是某个地址不用于发送数据,则每一个RTO都要对cwnd作一次调整,调整后的值为max(cwnd/2, 4*MTU).

 

3、紧急方式

       TCP提供了“紧急方式(urgentmode)”,它使链接的一端能够告诉另链接的一端有些 “紧急数据”已经被放置在数据流中。紧急数据的处理方式由接收方决定。
       要发送紧急数据须要设置TCP首部中的两个字段来。URG比特被置1,而且要将16bit的紧急指针设置为一个正的偏移量,该偏移量必须与TCP首部中的序号字段相加,以便得出紧急数据的最后一个字节的序号。
       TCP必须通知接收进程,什么时候已接收到一个紧急数据指针以及什么时候某个紧急数据指针还不在此链接上,或者紧急指针是否在数据流中向前移动。接着接收进程能够读取数据流,并必须可以被告知什么时候碰到了紧急数据指针。只要从接收方当前读取位置到紧急数据指针之间有数据存在,就认为应用程序处于“紧急方式”。在紧急指针经过以后,应用程序便转回到正常方式。

       没有办法指明紧急数据从数据流的何处开始。TCP经过链接传送的惟一信息就是紧急方式已经开始(TCP首部中的URG比特)和指向紧急数据最后一个字节的指针。其余的事情留给应用程序去处理。

相关文章
相关标签/搜索