3.TCP超时与重传---< >

3.1 引言

下层网络层(IP)可能会出现丢失,重复或则失序包的状况,TCP协议须要提供可靠的数据传输服务.为保证数据传输的正确性,TCP重传其认为已经丢失的包.TCP根据接收端返回至发送端的一系列确认信息来判断是否丢包.当数据段或确认信息失败,TCP启动重传操做,重传还没有确认的数据.重传的两套机制:1.基于时间,2.基于确认信息的构成(更高效)linux

1.RTO(retransmission timeout,重传超时):tcp在发送数据的时候设置一个计时器,若超时未收到数据确认信息,则会引起相应的超时或则基于计时器的的重传操做,这种计时器超时称为RTO(重传超时)算法

2.fast transmission(快速重传):若TCP累计确认没法返回新的ACK,或则当ACK包含的确认信息(SACK,selective acknowledge)代表出现失序报文段时,快速重传会推断出现丢包.缓存

3.2 简单的超时与重传举例

binary exponential backoff(二进制指数退避):每次重传间隔时间markdown

逻辑上讲,TCP拥有两个阀值来决定如何重传一个报文段.R1和R2网络

R1:TCP在向IP层传递"消极建议"(如从新评估当期那的IP路径)前,愿意尝试重传的次数(或则等待时间);数据传输段(SYN报文段)的R1>=3tcp

R2:(R2>R1)指示TCP应该放弃当前链接的时机.数据传输段的R2>=100s,TCP创建链接段的R2>=180s性能

linux中的R1和R2的设置,经过应用程序或则系统配置.参数 net.ipv4.tcp_retries1:重传次数,默认为3, net.ipv4.tcp_retries2:默认15,对应约为13~30分钟,根据具体的RTO而定, 对于SYN报文段net.ipv4.tcp_syn_retries和net.ipv4.tcp_synack_retries限定了重传次数,默认为5(约180s)优化

3.3 设置重传超时

TCP怎样根据给定链接的RTT设置RTO?若TCP先于RTT重传,可能会在网络中引入没必要要的重复数据,反之,若延迟至大于RTT的间隔发送数据,总体网络利用率就会降低.spa

TCP在收到数据以后会返回确认信息,所以可在该信息中携带一个字节的数据来测量传输该确认信息所须要的时间.每一个测量结果称为RTT样本(RTT sample).TCP首先须要根据一段时间内的样本值创建好估值,第二部是怎样根据估值设置RTO.code

3.3.1 经典方法

最初的TCP规范[RFC0793]采用如下公式计算获得平滑的RTT估值(称为SRTT):

SRTT <--- α(SRTT) + (1 - α)RTTs

  • SRTT是基于现存值和新样本RTTs的到的更新结果,这种测量方法称为指数加权移动平均(Exponentially Weightd Moving Average,EWMA)或则低经过滤(low pass filter)
  • α:平滑因子,推荐值为0.8-0.9,80% ~ 90%来自现存值,10% ~ 20%来自新测量值

考虑到SSRTT估计器的获得的估计值会随RTT为变化,[RFC0793]推荐根据以下公式设置RTO:

RTO = min(ubound,max(lbound,(SRTT)β))

  • β:时延离散因子,推荐为1.3 ~ 2.0
  • ubound:RTO上边界(可设置为建议值1分钟)
  • lbound:RTO下边界(可设置建议值,1秒)

咱们称为经典方法,他使得RTO的值设置为1秒,或约为两倍的SRTT

3.3.2 标准方法(Jacobson)

按照上述经典方法设置计时器,将没法适应RTT大规模变更(特别是当实际的RTT远大于估计值,会致使没必要要的重传).增大的RTT样本代表网络出现了过载,此时没必要要的重传会进一步加剧网络的负担.

为解决这一问题,可对原方法改进一适应RTT变更较大的状况.记录RTT测量值的变化状况以及平均值老获得较为准确的估计值.基于均值和估计值的变化来设置RTO,将比使用均值的常数倍来计算RTO更能适应RTT变化幅度较大的状况.

以下的算法采用了经典方法([RFC0793]),还同时考虑到了RTT样本变化值的方法计算RTO的对比状况.咱们将TCP获得的RTT测量样本值做为一统计过程,同时测量平均值和方差(或则标准差)能更好的估计未来值,还能够帮助TCP设置一个能适应大多数状况的RTO值.

平均误差(mean deviation)是对标准差的一种好的逼近,可是计算更容易,更快捷.这是由于计算标准差须要对方差的进行平方根运算,对于快速TCP实现来讲代价较大.所以咱们结合了平均值和平均误差来进行估值.对每一个RTT测量值M(前面的RTTs)采用如下公式:

srtt <--- (1 - g)(srtt) + (g)M

rttvar <--- (1 - h)(rttvar) + (h)(|M - srtt|)

RTO = srtt + 4(rttvar)

这里,srtt代替了以前的SRTT,而且rttvar为平均误差的EWMA,而非采用以前的β来设置RTO.上述等式也能够写成另一种形式,对计算机实现来讲较为方便:

Err = M - srtt

srtt <--- srtt + g(Err)

rttvar <--- rttvar + h(|Err| - rttvar)

RTO = srtt + 4(rttvar)

  • srtt:平均值的EWMA,参与计算RTO,而且随时间变化
  • rttvar:绝对偏差|Err|的EWMA,参与计算RTO,而且随时间变化
  • Err:测量值M与当前RTT估值srtt以前的误差
  • g:增量g,新RTT样本M占srtt估计值的权重,取值1/8,取值为2的(负的)多少次方
  • h:增量h为新平均误差样本(新样本M与当前平均值srtt以前的绝对偏差)占误差估计值rttvar的权重,取值1/4,取值为2的(负的)多少次方

当RTT变化时,误差的增量越大,RTO增加越快.

经典方法和Jacobson计算方法的区别:

  • 平均RTT的计算过程相似(α 等于 1 - g),采用的增量不一样.
  • Jacobson同时基于平滑RTT和平滑误差计算RTO,而经典方法简单的采用平滑RTT的倍数,这是迄今为止许多TCP实现RTO的方法.

3.3.2.1 时钟粒度与RTO边界

在测量RTT的过程汇总,TCP时钟时钟处于运行状态.对于初始序列号码来讲,实际的TCP链接的时钟并不是从零开始,也没有绝对精确的精度,TCP的时钟一般为某个变量,该变量随着系统时钟而作出更新,但并不是一对一地同步更新.TCP时钟一个"滴答"的时间一般称为粒度.一般,该值相对较大(约500ms),但新的时钟粒度更细(linux采用了1ms)

粒度会影响RTT的测量值以及RTO的设置.粒度用于优化RTO的更新状况,并给RTO设置了一个下界.计算公司以下:

RTO = max(srtt + max(G,4(rttvar)),1000)

  • G:计时器的粒度,1000ms为整个RTO的下界值,所以RTO至少为1s,可选上届值

3.3.2.2 初始值

在首个SYN交换以前,除非系统提供,不然TCP没法设置RTO初始值.根据[RFC6298],RTO的初始值为1s,而初始SYN报文段采用的超时时间为3s.当收到首个RTT测量结果M,估计器按照如下方式进行初始化:

srtt <--- M

rttvar <--- M/2

3.3.2.3 重传二义性与Karn算法

重传二义性:在测量RTT样本的过程当中若一个包的传输出现超时,该数据就会被重传,接着收到一条ACK信息,那么该ACK是对第一条仍是第二条传输的确认就存在这二义性.

Karn算法

  • [KP87]指出,出现超时,接收到重传数据的确认信息时不能更新RTT估计值.这是Karn算法的第一部分.经过排除二义性数据来解决RTT估计值出现的二义性问题.

  • TCP在计算RTO的过程当中采用一个退避系数(backoff factor),每当重传计时器出现超时,退避系数加倍,该过程一直持续到非重传数据.此时退避系数为1,重传计时器返回正常值.对重传过程退避系数加倍,这是Karn算法的第二部分.若TCP超时,同时会引起拥塞控制机制,以此改变发送速率.[KP89]中所述:

当接收到重复传输(即至少重传一次)数据的确认信息时,不进行该数据包的RTT测量,能够避免重传二义性问题.另外,对该数据以后的包采起退避策略.仅当接受到未经重传的数据时.该SRTT才用于计算RTO.

3.3.2.4 带时间戳选项的RTT测量

TCP时间戳选项(TSOPT)做为PAWS算法的基础,还能够做RTT测量(RTTM).容许发送者在返回的对应确认信息中携带一个32比特的数.

时间戳值(TSV)携带初始SYN的TSOPT中,并在SYN+ACK的TSOPT的TSER部分返回,以此设定srtt,rttvar与RTO的初始值.

当传输大批量数据时,TCP一般采起每两个报文段返回一个ack的方法,当数据出现丢失,失序或则重传成功时,TCP的累计确认机制报文段与其ACK之间的不是一一对应关系.为了解决这些问题,使用时间戳选项的TCP采用如下算法来测量RTT样本值:

  • 1.TCP发送端在其发送的的每一个报文段的TSOPT的TSV部分携带一个32比特的时间戳值.该值包含数据发送时刻的TCP时钟值.
  • 2.接收端记录接收到的TSV值(TsRecent的变量)并在对应的ACK中返回,而且记录其上一个发送的ACK号(LastAck的变量).ACK号表明接收端(ACK发送方)指望接收到的下一个有序序号.
  • 3.当一个新的报文段到达时,若是其序列号码与LastACK的值吻合(即为下一个指望接收的报文段),则将其TSV值存入TsRecent.
  • 4.接收端发送的任何一个ACK都包含TSOPT,TsRecent变量包含的时间戳被写入其TSER部分.
  • 5.发送端收到ACK后,将当前的TCP时钟减去TSER值,获得的差值即为新的RTT样本估计值.

3.3.3 Linux 采用的方法

RTO一般设置为srtt+4(rttvar),不管最大RTT样本的值是大于仍是小于srtt,rttvar的任何大的变化,都会致使RTO的增大.Linux经过减少RTT样本值大幅减少rttvar的影响来解决这一问题.Linux设置RTO的方法:

与标准方法同样,Linux也记录srtt和rttvar值,但还同时记录两个新的变量mdev和mdev_max.

  • mdev:标准方法的瞬时平均误差估计值,即上面的rttvar
  • mdev_max:记录测量RTT样本过程当中的最大mdev,最小值为50ms.rttvar需按期更新一保证以保证其不小于mdev_max.所以RTO不会小于200ms.

Linux根据mdev_max的值来更新rttvar.RTO老是等于srtt与4(rttvar)之和,以此保证RTO不超过TCP_RTO_MAX(默认值为120s).以下图.

  • 首次RTT样本测量

  • srtt = 16ms

  • mdev = (16/2)ms = 8s

  • rttvar = mdev_max = max(mdev,TCP_RTO_MIN) = max(8,50)

  • RTO = srtt + 4(rttvar) = 16 + 4(50) = 216ms

在初始SYN交换以后,发送端对接收端的SYN返回一个ACK,接收端进行了一次窗口更新.这些包未包含数据数据(SYN或则FIN字段,但算做数据),而且没有记录相应的时间,而且发送端接收到窗口更新时也没有进行RTT更新.TCP对不含数据的报文段不提供可靠性传输,意味着若出现丢包也不会重传,所以无需设定重传计时器.

注意:TCP选项自己并不进行重传或可靠性传输.仅仅当数据段(包含SYN和FIN报文段)中明确设定,才会丢失重传,但也仅做为反作用.

当应用首次执行写操做,发送端TCP发送两个报文段,每一个报文段包含一个值为127的TSV.因为两次发送间隔小于1ms(发送端的TCP发送粒度),所以两个值相等.当发送端以这种方式发送多个报文段时,能够看到之中并无前进的状况.

接收端变量LastACK记录上一次发送的ACK的序列号码.本例中,上一个发送的ACK为链接创建阶段的SYN + ACK包,所以AC从1开始.首个全长(full-size)报文段到达,其序列号与LastACK吻合,则将TsRecent变量更新为接收分组的TSV,即127.第二个报文段的到达并无更新TsRecent,由于其序列号码字段与LastACK不匹配.接收端返回对应分组的ACK时,需在其TSER部分包含TsRecent,同时接收端还要更新LastACk变量的ACK号为2801.

  • 第二次RTT样本测量

样本m = 223 - 127 = 96

  • mdev = mdev(3/4) + |m - srtt|(1/4) = 8(3/4) + |80|(1/4) = 26ms

  • mdev_max = max(mdev_max,mdev) = max(50,26) = 50ms

  • srtt = srtt(7/8) + m(1/8) = 16(7/8) + 96(1/8) = 14 + 12 = 26ms

  • rttvar = mdev_max = 50ms

  • RTO = srtt + 4(rttvar) = 26 + 4(50) = 226ms

标准方法中rttvar所占权重较大(系数为4),所以当RTT减少时,也会致使RTO增加.在时钟粒度较大时(500ms),不会有太大的影响,由于RTO可用值不多.可是,若时钟颗粒较细,好比Linux的1ms,可能出问题.针对RTT减少的状况,若新样本值小于RTT估值范围的下界(srtt - mdev),则减少样本的权重.伪代码以下:

if(m < (srtt - mdev))
    mdev = (31/32) * mdev + (1/32)*|srtt -m|
else
    mdev  = (3/4)* mdev + (1/4) * |srtt - m|
复制代码

3.5 快速重传

快速重传机制基于接收端的反馈信息来引起重传,而非重传计时器的超时.与超时重传相比,快速重传能更加及时有效地修复丢包状况.

快速重传算法:TCP发送端在观测到至少dupthresh(重复ACK的阀值)个重复ACK后,即重传可能丢失的数据分组,而没必要等到重传计时器超时.固然也能够同时发送新数据.根据重复ACK推断的丢包一般与网络拥塞有关,所以伴随快速重传应促发拥塞控制机制.不采用SACK时,在接受到有效ACK前至多只能重传一个报文段.采用SACK,ACK可包含额外信息,使得发送端在每一个RTT时间内能够填补多个空缺.

3.6 带选择确认的重传

随着选择确认选择的标准化,TCP接收端可提供SACK功能,经过TCP头部累计的ACk字段来描述描述其接受到的数据.ACK号与接收端缓冲中的其余数据之间的间隔称为空缺.序列号高于空缺的数据称为失序数据,由于这些数据和以前接受的序列号码不连续.

TCP发送端的任务是经过重传丢失的数据来填补接收端缓冲中的空缺,但同时也要尽量保证不重传正确接收到的数据.合理采用SACK信息可以更快地实现空缺填补,且可以较少没必要要的重传,缘由在于其在一个RTT内可以获知过个空缺.当采用SACK选项时,一个ACK可包含三四个告知失序数据的SACK信息.每一个SACK信息包含32位的序列号,表明接收端存储的失序数据的起始至最后一个序列号.

SACK选项指定n个块的长度为8n+2字节,所以40字节可包含4个块.一般SACK会与TSOPT一同使用,所以须要额外的10个字节(外加2字节的填充数据),这意味着SACK在每一个ACK中只能包含3个块.

3.6.1 SACK接收端的行为

接收端在TCP链接创建期间收到SACK许可选项便可生成SACK.一般来讲,每当缓存中存在失序数据时候,接收端就能够生成SACK.

第一个SACk块内包含的是最近接收到的报文段的序列号码.因为SACK选项的空间有限,应尽量确保向TCP发送端提供最新的信息.其他的SACK块包含的内容也按照接收到的前后依次排列.也就是说,最新的一个块包含的内容除了包含接受到的序列号,还须要重复以前的SACK块.

在一个SACK选项中包含多个SACK块,而且在多个SACK中重复这些块信息的目的是为了防止SACK丢失提供一些备份.

3.6.2 SACK发送端的行为

在发送端也应该提供SACK功能,而且合理地利用接收到的SACK块来进行丢包重传,该过程称为选择性重传(selective retransmission)选择性重发(selective repeat).SACk发送端记录接收到的累计ACK信息,还须要记录接收到的SACK信息,并利用该信息来避免重传正确接受的数据.

当SACk发送端执行重传时,一般是因为接收到了SACK或则重复的ACK,它能够选择发送新数据或则旧数据.SACK信息提供接受端数据的序号范围,所以发送端可据此推断须要重传的空缺数据.最简单的办法是使发送端首先填补接收端的空缺,而后再继续发送新数据.这是经常使用的方法.

3.7 伪超时与重传

在不少状况下,即便没有出现数据的丢失也可能引起重传.这种没必要要的重传称为伪重传(spurious retransmission),主要缘由是伪超时过早判断超时,其余缘由包括失序,包重复,或则ACK丢失.在实际RTT显著增加,超过当前RTO时,可能出现伪超时.在下层协议性能变化较大的环境中(无线环境),这种状况出现的比较多.

伪超时的解决方法,一般包含检测算法(detection)响应算法(response)

3.7.2 Eifel检测算法

实验性质的Eifel检测算法利用了TCP的TSOPT来检测伪重传.在发生超时重传后,Eifel算法等待接收下一个ACK,若对第一次传输的确认,则判断该重传是伪重传.

Eifel检测算法的机制很简单.它须要使用TCP的TSOPT.当发送一个重传后,保存器TSV值.当接收到的相应分组的ACK后,检测该ACK的TSER部分.若TSER部分小于以前的存储的TSV值,则可判断该ACK对应的是原始的传输分组,即该分组是伪重传.

3.7.3 前移RTO恢复(F-RTO)

**前移RTO恢复(Forward-RTO Recovery,F-RTO)**是检测伪重传的标准算法.该算法只检测由重传计时器超时引起的伪重传;对于其余缘由引起的伪重传则没法判断.

F-RTO会修改TCP的行为,在超时重传以后接受到的第一个ACK时,TCP会发送新(非重传)数据,以后再响应一个到达的ACK.若是其中有一个重复ACK,则认为这次重传没有问题. 若是这两个都不是重复ACK,则表示该重传是伪重传.若是新数据的传输获得了相应的ACK,就使得接收端窗口前移.若是新数据的发送致使了重复的ACK那么接收端至少有一个或更多的空缺.这两种状况,接收新数据都不会影响总体数据的传输性能.

3.7.4 Eifel相应算法

一旦判断出现伪重传,则会引起一套标准操做,即为Eifel响应算法.在重传计时器超时后,它会检查srtt和rttvar的值,并按照以下的方式记录新的变量srtt_prev和rttvar_prev:

  • srtt_prev = srtt + 2(G)

  • rttvar_prev = rtvar

  • G:TCP时钟粒度
  • srtt_prev:设为srtt加上两倍的时钟粒度,由于srtt太小,可能出现伪超时.若是srtt稍大,就可能发生超时.

完成srtt_prev和rttvar_prev的存储以后,就要触发某种检测算法.运行检测算法以后可获得一个特殊值,称为伪恢复(SpuriousRecovery).若是检测到一次伪超时,则将伪恢复置为SPUR_TO.若是检测到迟超时,则将其置为LATE_SPUR_TO.不然超时,改次超时为正常超时,TCP继续执行正常的响应行为.

若为伪恢复SPUR_TO,TCP可在恢复阶段完成以前进行操做.将下一个要发送报文段的序列号修改成最新的未发送过报文段.这样能够避免"回退N"行为.

若检测到一次迟到伪超时,此时已生成对首次的重传的ACK,则SND.NXT不改变.在以上两种状况中,都要从新设置拥塞控制状态.而且一旦接收到重传计时器超时后的发送的报文段ACk,就要按照以下方式更新srtt,rttvar和RTO

  • srtt <--- max(srtt_prev,m)

  • rttvar <---max(rttvar_prev,m/2)

  • RTO = srtt + max(G,4(rttvar))

  • m:RTT样本值,超时后首个发送数据接收到的ACK计算获得的.
相关文章
相关标签/搜索