一、概述

我们之前在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. JavaScript入门学习(0)相关 软件工具

      JavaScript本地脚本编辑工具(1st JavaScript Editor Pro ) 必要设置     https://pan.baidu.com/s/1XoaNA9o0qt2eJfLgoZ5 ...

    2. C++的一些关键字用法

      const 这个关键字真是太常用了, 所以干脆总结一下. int const a = 8; //定义一个int常量a, 不能再给a赋值了 const int a = 8; //和上面一样 int co ...

    3. PTA(Basic Level)-1002 写出这个数

      一 1002 写出这个数  读入一个正整数 n,计算其各位数字之和,用汉语拼音写出和的每一位数字. 输入格式: 每个测试输入包含 1 个测试用例,即给出自然数 n 的值.这里保证 n 小于 10​10 ...

    4. 磁砖样式——第八届蓝桥杯C语言B组(国赛)第二题

      原创 标题:磁砖样式 小明家的一面装饰墙原来是 3*10 的小方格.现在手头有一批刚好能盖住2个小方格的长方形瓷砖.瓷砖只有两种颜色:黄色和橙色. 小明想知道,对于这么简陋的原料,可以贴出多少种不同的 ...

    5. [agc003F]Fraction of Fractal

      Description 传送门 Solution 本篇博客思路来自大佬的博客(侵删). 我们定义如果网格的第一行和最后一行的第i列都为黑色,则它是一个上下界接口.左右界接口定义同上. 如果上下界接口和 ...

    6. 【BZOJ3527】[ZJOI3527]力

      [BZOJ3527][ZJOI3527]力 题面 bzoj 洛谷 题解 易得 \[ E_i=\sum_{j<i}\frac{q_j}{(i-j)^2}-\sum_{j>i}\frac{q_ ...

    7. HBase第一章 安装 HMaster 主备

      1.集群环境 Hadoop HA 集群规划 hadoop1 cluster1 nameNode  HMaster hadoop2 cluster1 nameNodeStandby ZooKeeper ...

    8. python开发ftp服务器第一天(pyftpdlib)

      学习了大约快一个月的python,现在开始有意识做一些项目.(我的新书<Python爬虫开发与项目实战>出版了,大家可以看一下样章) 据我了解,python现在更多的是用于自动化运维方面, ...

    9. 测试基础-http协议(转)

      HTTP的特性 HTTP构建于TCP/IP协议之上,默认端口号是80 HTTP是无连接无状态的 HTTP报文 请求报文 HTTP 协议是以 ASCII 码传输,建立在 TCP/IP 协议之上的应用层规 ...

    10. MySQL☞左外链接与右外连接

      外链接查询:即要查询有关联关系的数据,还要查询没有关联关系的数据.(个人理解为:表A和表B两个关联的列中)如下图: emmm,简单的来说两个表的关联关系:book.bid=bookshop.id,他们 ...