一、概述

我们之前在SACK关闭场景下的拥塞撤销那篇文章中提到过Eifel探测算法(Eifel Detection Algorithm),最早在介绍DSACK和FRTO的时候我们就有提到过Eifel探测算法。Eifel探测算法是基于TSopt选项中TSV的单调非减特性设计的。简单介绍一下Linux中Eifel探测算法的实现,Linux会在TCP进行第一次重传的时候把重传数据包的TSV记录在状态变量retrans_stamp中,当收到partial ACK的时候,或者收到的Ack报文的ack number>=high_seq的时候,如果这个报文的TSER<retrans_stamp,说明这个ACK报文是对应TCP初传(称呼为original transmit)的确认包。

从上面的原理介绍可以看到Eifel探测可以用于虚假超时重传和虚假快速重传,而且即使发生ACK丢包,Eifel探测算法仍然可以使用随后ACK报文的TSER进行探测。本文重点介绍一下虚假快速重传后partial ACK触发的拥塞撤销。

Recovery状态下TCP收到partial ACK的时候,如果当前拥塞状态没有撤销(undo_marker标记非0),并且Eifel探测检测到虚假重传,那么TCP按照如下步骤处理(注意这是一个根据实现代码     #参考本系列destination metric文章

  • ******@Inspiron:~$ sudo ethtool -K lo tso off gso off  #关闭tso gso以方便观察cwnd变化
  • 业务模型:server端和client建立连接后,首先休眠1002ms,然后以5ms为间隔连续写入17个数据包,每个数据包的大小为50bytes,其中数据包传输发生了乱序,对于No7-No14这几个数据包,client收包顺序为No9、No10、No11、No12、No7、No14、No8、No13,其余数据包则顺序到达client端,client端对每个收到的数据包都会回复一个ACK确认包。最终业务情况如下面的wireshark图示,No7、No8、No13、No14这几个乱序包高亮标出来了。注意下面拥塞控制状态的变化为:Open->Disorder->Recovery->Disorder->Open

    No1-No18:这种类似的初始化慢启动前面已经介绍过多次,这里不再进行介绍,最终server端发出No18数据包后,ssthresh=0x7fffffff(我们在上面的路由表中并没有设置ssthresh的参数,这个是个默认初始值),cwnd=11,packets_out=11,sacked_out=0, lost_out=0, retrans_out=0, fackets_out=0, prr_delivered=0, prr_out=0,server端TCP处于Open状态。

    No19-No25:No19、No21、No23、No24这四个确认包分别对应No9、No10、No11、No12,这种FACK快速重传也介绍过多次了,这里不再废话,最终server发出No25后,prior_ssthresh=0x7fffffff,prior_cwnd=11,ssthresh=5,cwnd=8,packets_out=12,sacked_out=4, lost_out=2, retrans_out=2, fackets_out=6, prr_delivered=3, prr_out=2,high_seq=651,retrans_stamp=2409052(No22的TSVal),undo_marker标记有效(即值非零),server端TCP处于Recovery状态。

    No26:No26是对应乱序后的No7数据包的,是一个partial ACK,server端的TCP收到这个partial ACK的时候,首先更新packets_out=11, fackets_out=5,lost_out=1,retrans_out=1,接着按照我们上面介绍的流程处理,首先undo_marker非零,表示当前还没有进行过拥塞撤销,接着No26中的TSecr=2409036<retrans_stamp=2409052,因此Eifel探测算法判断这个partial ACK是对应初传数据包的,之前的快速重传为虚假快速重传,接着按照上面介绍的处理流程更新dupthresh=fackets_out+acknum=6,此时retrans_out>0,因此按照之前文章介绍的Recovery状态cwnd更新流程来更新拥塞窗口。prr_delivered=4,in_flight=11-(4+1)+1=7,delta=5-7<0,sndcnt = (ssthresh * prr_delivered + prior_cwnd - 1)/prior_cwnd - prr_out=(5*4+10)/11-2=0, sndcnt=max(0,0)=0,因此cwnd=in_flight+sndcnt=7。随后server端尝试发送新数据包,但是受限于cwnd未能发出。注意在Eifel探测到虚假重传后server端不再尝试重传TCP数据。

    No27-No28:No27对应乱序的No14数据包,通过SACK确认了No14数据包,因此更新sacked_out=5,fackets_out=7,No27的ack number并没有发生变化,因此并不是partial ACK,因此会按照Recovery状态下普通的dup ACK处理流程来处理。server端接着进入Recovery状态下的cwnd更新流程,prr_delivered=5,in_flight=11-(5+1)+1=6,delta=-1<0,sndcnt = (5*5+10)/11-2=1, sndcnt=max(1,0)=1,因此cwnd=in_flight+sndcnt=7。拥塞窗口允许发出一个TCP数据包,此时尝试快速重传没有对应的需要重传的数据包,接着发出新数据包No28,更新packets_out=12,prr_out=3。

    No29-No32:No29仍然是一个partial ACK,ack number新确认了5个数据包,server端收到这个partial ACK后,更新packets_out=7,sacked_out=1, lost_out=0, retrans_out=0, fackets_out=2,接着进入partial ACK的处理流程,No29的TSecr=2409037<retrans_stamp=2409052,因此Eifel探测认为之前快速重传为虚假重传,而且此时undo_marker非零,因此进入上面介绍的处理流程,首先dupthresh=6不变,此时retrans_out=0,因此把标记为lost的数据包取消lost标记,更新cwnd=max(cwnd,2*ssthresh)=10, ssthresh=max(ssthresh,prior_ssthresh)=0x7fffffff,设置undo_marker=0,防止后面重复拥塞撤销。此时sacked_out>0,因此server端TCP从Recovery状态切换到DIsorder状态。接着进入reno的慢启动过程,No29的ack number新确认了5个数据包,因此更新cwnd=cwnd+5=15,更新。最后发出新数据包No30、No31、No32,此时server端TCP中已经没有待发送的数据了。发出No32后,ssthresh=0x7fffffff,cwnd=15,packets_out=10,sacked_out=1, lost_out=0, retrans_out=0, fackets_out=2。

    No33-No37:client端的ack确认包,server端在收到No37后,ssthresh=0x7fffffff,cwnd=21,packets_out=4,sacked_out=1, lost_out=0, retrans_out=0, fackets_out=0。

    No38-No39:注意这两个数据包是对应虚假快速重传No22和No25的,server端在收到No38这个dup ACK后并不会进入Disorder状态,原因是当前SACK处于打开状态,进入Disorder的条件是有SACK信息,这里还有两个点需要注意,首先注意这里反馈的确认包的ack number与之前的high_seq相同,如果是SACK关闭的场景,这里如果有dupthresh次以上的dup ACK就会再次触发虚假快速重传,这里要和我们之前文章介绍的SACK关闭场景下false fast retransmit的避免算法进行对比。另外一点就是在SACK打开的场景下,收到这样的dup ACK并不会触发快速重传,后面我们介绍TLP的时候会演示这种场景,这也就是之前的false fast retransmit的避免算法只需要处理SACK关闭场景的原因。

    No40-No42:最终server发出No43后,ssthresh=0x7fffffff,cwnd=24,packets_out=1,sacked_out=0, lost_out=0, retrans_out=0, fackets_out=0。

    No43:server在收到这个ACK后,发现当前处于application-limited传输状态(参考前面CWV的介绍文章),因此并不会更新cwnd,最终 ssthresh=0x7fffffff, cwnd=24, packets_out=0, sacked_out=0,  lost_out=0, retrans_out=0, fackets_out=0。

    最后同样给出本示例的系列号时序图

    2、SACK关闭场景下的Eifel探测与拥塞撤销

    还记得我们之前在演示SACK关闭场景下的拥塞撤销示例的时候设置tcp_timestamps=0关闭了TSopt选项吧。原因是如果不关闭的话收到partial ACK的时候Eifel探测算法就会进行拥塞撤销操作。下面给出一个SACK关闭场景下Eifel探测的示例,下面示例中tcp_sack=0,initcwnd=5,congctl=reno,关闭GSO、TSO功能,其中No7发生乱序传输,client先收到No8、No9、No10然后在收到的No7。整体处理比较简单不再进行逐包解释了。

    补充说明:

    1、partial ACK下拥塞撤销的详细流程请参考tcp_try_undo_partial。

    TCP系列50—拥塞控制—13、Eifel探测下的拥塞撤销的更多相关文章

    1. TCP系列49—拥塞控制—12、DSACK下的拥塞撤销

      一.概述 DSACK下的虚假重传的检测我们之前重传部分的文章已经介绍过了,这里简单说一下拥塞控制部分的实现. linux内部会维护一个undo_retrans状态变量,其值为已经重传的次数减掉被DSA ...

    2. TCP系列45—拥塞控制—8、SACK关闭的拥塞撤销与虚假快速重传

      一.概述 这篇文章介绍一下TCP从Recovery状态恢复到Open状态的时候cwnd的更新.我们在tcp重传部分的文章中曾经介绍过虚假重传的概念,Linux在探测到虚假重传的时候就会执行拥塞撤销操作 ...

    3. TCP系列46—拥塞控制—9、SACK下的快速恢复与Limited transmit

      一.概述 1.SACK下的特殊处理过程 SACK下的拥塞控制处理是linux中拥塞控制的实现依据,再次强调一遍RFC6675的重要性,linux中拥塞控制主体框架的实现是与RFC6675一致的,所以如 ...

    4. TCP系列40—拥塞控制—3、慢启动和拥塞避免概述

      本篇中先介绍一下慢启动和拥塞避免的大概过程,下一篇中将会给出多个linux下reno拥塞控制算法的wireshark示例,并详细解释慢启动和拥塞避免的过程. 一.慢启动(slow start) 一个T ...

    5. TCP系列47—拥塞控制—10、FACK下的快速恢复与PRR

      一.概述 FACK下的重传我们在之前的重传部分已经进行了介绍,这里简单介绍一下随着FACK提出的拥塞控制算法的改进及随后的进一步改进. 从我们之前介绍的RFC2582和RFC5681中可以看到,快速恢 ...

    6. TCP系列48—拥塞控制—11、FRTO拥塞撤销

      一.概述 FRTO虚假超时重传检测我们之前重传章节的文章已经介绍过了,这里不再重复介绍,针对后面的示例在说明两点 1.FRTO只能用于虚假超时重传的探测,不能用于虚假快速重传的探测. 2.延迟ER重传 ...

    7. TCP系列54—拥塞控制—17、AQM及ECN

      一.概述 ECN的相关内容是在RFC3168中定义的,这里我简单描述一下RFC3168涉及的主要内容. 1.AQM和RED 目前TCP中多数的拥塞控制算法都是通过缓慢增加拥塞窗口直到检测到丢包来进行慢 ...

    8. TCP系列23—重传—13、RACK重传

      一.RACK概述 RACK(Recent ACKnowledgment)是一种新的基于时间的丢包探测算法,RACK的目的是取代传统的基于dupthresh门限的各种快速重传及其变种.前面介绍的各种基于 ...

    9. TCP系列51—拥塞控制—14、TLP、ER与拥塞控制

      一.概述 这里的重点是介绍TLP.ER与拥塞控制并不是介绍TLP和ER本身,因此TLP和ER的详细内容请翻前文. 在TLP与拥塞控制的交互中有几个点需要注意 1.TLP触发的重传后,TCP仍然处于Op ...

    随机推荐

    1. hive 优化 (转)

      Hive优化 Hive优化目标 在有限的资源下,执行效率更高 常见问题 数据倾斜 map数设置 reduce数设置 其他 Hive执行 HQL --> Job --> Map/Reduce ...

    2. C语言编程练习 GPS数据处理

      题目内容: NMEA-0183协议是为了在不同的GPS(全球定位系统)导航设备中建立统一的BTCM(海事无线电技术委员会)标准,由美国国家海洋电子协会(NMEA-The National Marine ...

    3. 20155231 2016-2017-2《Java程序设计》课程总结

      20155231 2016-2017-2<Java程序设计>课程总结 每周作业链接汇总 预备作业1:师生关系 预备作业2:优秀技能经验 预备作业3:虚拟机linux初接触 第一周作业:认识 ...

    4. 20155331 实验四 Android开发基础

      20155331丹增旦达实验四报告 实验四 Android程序设计-1 Android Stuidio的安装测试: 参考<Java和Android开发学习指南(第二版)(EPUBIT,Java ...

    5. 【BZOJ4016】[FJOI2014]最短路径树问题

      [BZOJ4016][FJOI2014]最短路径树问题 题面 bzoj 洛谷 题解 虽然调了蛮久,但是思路还是蛮简单的2333 把最短路径树构出来,然后点分治就好啦 ps:如果树构萎了,这组数据可以卡 ...

    6. SaltStack入门篇(七)之架构部署实战

      模块:https://docs.saltstack.com/en/2016.11/ref/states/all/index.html 实战架构图: 实验环境设置: 主机名 IP地址 角色 linux- ...

    7. 字典(dict)的反转

      1.今天在写12306查询余票时,想给定字典(dict)的值,从而得到字典(dict)的键,但好像字典(dict)方法中没有与此相关的方法,只能退而求其次,反转字典(dict),将原字典(dict)的 ...

    8. autoreleasepool 自动释放池的理解

      常见的面试题:以下代码存在什么样的问题?应该如何改进? for (int i = 0; i < 100000; i++) { NSString *str = @"abc"; ...

    9. 【excle基础】如何去掉excel某一列中的字段的空格

      如图所示,想要去掉A列的空格: 查找空格,全部替换

    10. 世界杯足彩怎么买划算?机器学习AI告诉你答案(含预测)

      本文首发于InfoQ公众号头条. 四年一度的世界杯又来了,作为没什么时间看球的码农,跟大家一样,靠买买足彩给自己点看球动力和乐趣, 然而总是买错球队,面对各种赔率也不知道怎么买才划算,足彩是不是碰大运 ...