一、概述

FACK下的重传我们在之前的重传部分已经进行了介绍,这里简单介绍一下随着FACK提出的拥塞控制算法的改进及随后的进一步改进。

从我们之前介绍的RFC2582和RFC5681中可以看到,快速恢复下当探测到丢包的时候,会设置ssthresh = max (FlightSize / 2, 2*MSS)、 cwnd=ssthresh+3*MSS,随后发送端收到dup ACK的时候进行cwnd的inflate过程,发送端需要收到大约一半的dup ACK后,才能允许发送新数据,这意味着发送端需要等待大约RTT/2的时间后才能发送新数据,然后随后的RTT/2时间内每收到一个dup ACK就发送一个新数据包。为了避免初始的RTT/2等待并把发送过程平稳化,Mathis等人在1996年Mathis提出FACK的同时提出了overdamping和rampdown,随后作者把这两个算法统一并改进为rate halving,然后作者又给rate halving加了一些门限参数,提出了一个 ssthresh lock 30     #参考本系列destination metric文章

  • ******@Inspiron:~$ sudo ethtool -K lo tso off gso off  #关闭tso gso以方便观察cwnd变化
  • 1、FACK下的快速恢复

    在介绍本示例前如果读者不熟悉linux的TCP代码,那一定要先看前面的文章。

    业务场景:server端与client建立连接后休眠1000ms,接着连续发出12个数据包,每个数据包的大小为50bytes,发送间隔为3ms,在第12数据包write写入后,立即进行一次write操作写入(15*50)bytes。server端的发送缓存设置足够大,因此write操作不会因为发送缓存不足而休眠。server发出的数据包中有5个数据包在传输过程中丢失,如下图所示,我高亮标示出来的No8、No9、No10、No11、No19这五个数据包传输丢失。client对收到的每个数据包都会进行ACK回复确认。

    No1-No23:这些数据包的处理与前面SACK下的快速恢复示例1的处理相同,因此不再重复介绍。发出No23后,packets_out=14,sacked_out=0, lost_out=0, retrans_out=0,fackets_out=0,prr_delivered=0, prr_out=0,ssthresh=30, cwnd=14,server端TCP处于Open状态。

    No24-No25:No8、No9、No10、No11这四个数据包丢失,client收到乱序的No12后回复No24确认包,No24通过SACK确认了No12数据包。client按序接收的数据包到No7为止,乱序接收了No12,因此更新fackets_out=12-7=5,表示server端当前接收到的最靠前的数据包No12与No7之间差了5个数据包(包括No12本身)。同样会更新sacked_out=1。此时dupthresh=3,在打开FACK的情况下,server端会判断如果fackets_out>dupthresh,就表示需要进行快速重传(请参考前面FACK重传的介绍文章),因此server端从Open状态直接切换到Recovery状态,注意这个切换中间是不经过Disorder状态的,进入Recovery状态的时候更新prior_ssthresh=max(ssthresh,3/4*cwnd)=30, ssthresh=max(cwnd/2,2)=7, prior_cwnd=cwnd=14, prr_delivered=0, prr_out=0,high_seq=801。因为fackets_out-dupthresh=2,因此server端TCP会把系列号范围(101,151)、(151,201)这两个数据包标记为lost,更新lost_out=2。接着进入Recovery状态下更新cwnd的流程,更新prr_delivered=1,此时in_flight= packets_out - ( sacked_out + lost_out) + retrans_out = 14-(1+2)+0=11,delta=sshresh-in_flight=7-11=-4<0,因此sndcnt = (ssthresh * prr_delivered + prior_cwnd - 1)/prior_cwnd - prr_out =(7*1+14-1)/14-0=1, sndcnt = max(sndcnt, (fast_rexmit ? 1 : 0)) = max(1, 1)=1,因而更新cwnd = in_flight + sndcnt = 12。接着server端进入快速重传流程,此时虽然有两个数据包被标记为lost但是因为cwnd的限制只能重传一个数据包,即No25,然后更新retrans_out=1, prr_out=1。随后server端尝试发送新的数据包同样由于cwnd限制未能发出。

    No26:server端收到No26后,No26通过SACK块新确认了一个数据包,因此更新sacked_out=2,而No26通过SACK确认的最靠前的数据包为(351,401),与系列号Ack=101相差6个数据包,因此更新fackets_out=6,fackets_out-dupthresh=3,因此从Ack=101开始的三个数据包可以标记为lost,(101,151)和(151,201)这两个数据包在之前已经标记为lost了,因此本次只是额外标记(201,251)为lost,更新lost_out=3。接着进入cwnd的更新,prr_delivered=2,in_flight=14-(2+3)+1=10,delta=-3,sndcnt=(7*2+14-1)/14-1=0,sndcnt=max(0,0)=0,因此cwnd=10。拥塞控制不允许发出新的数据包,故而接下来的快速重传和新数据发送尝试都没有发出数据包。

    No27-No28:server端收到No27后更新sacked_out=3, fackets_out=7,lost_out=4,prr_delivered=3,in_flight=14-(3+4)+1=8,delta=-1,sndcnt=(7*3+14-1)/14-1=1,sndcnt=max(1,0)=1,因此cwnd=9。此时拥塞控制允许发出一个数据包,因此随后的快速重传流程发出了No28数据包,并更新prr_out=2,retrans_out=2。

    No29:server端收到No29后更新sacked_out=4, fackets_out=8, 此时fackets_out-dupthresh=5,也就意味着从Ack=101开始的5个数据包应该被标记为lost,但是第5个数据包的系列号为(301,351),这个数据包已经被SACK确认了,因此不会被标记为lost状态,lost_out因而不会更新。接着进入更新cwnd的流程,prr_delivered=4,in_flight=14-(4+4)+2=8,delta=-1<0,sndcnt=(7*4+14-1)/14-2=0,sndcnt=max(0,0)=0,因此cwnd=8,此时拥塞控制不允许发出数据包。

    No30:server收到No30后,sacked_out=5,fackets_out=9,lost_out=4保持不变原因同上,prr_delivered=5,in_flight=14-(5+4)+2=7, delta=0, 此时delta不再小于0,因此sndcnt的更新流程也发生了变化,sndcnt = min(delta, newly_acked_sacked)=min(0,1)=0, sndcnt=max(0,0)=0,因此cwnd=7,此时同样不允许发出新的数据包。

    No31-No32:server端收到No31后,sacked_out=6,fackets_out=10,lost_out=4,prr_delivered=6,in_flight=14-(6+4)+2=6, delta=1, sndcnt=min(1,1)=1, sndcnt=max(1,0)=1,因此cwnd=7,此时拥塞窗口允许发出一个数据包,接着server端快速重传No32数据包并更新prr_out=3,retrans_out=3。

    No33-No34:这组数据包的处理与No31-No32类似,server收到No33后,sacked_out=7,注意No33这个ACK确认包的SACK块相比No31新确认数据包(651,701),701这个系列号相比No31的601系列号向前滑动了两个数据包的大小,因而更新fackets_out=fackets_out+2=12, lost_out=4, prr_delivered=7, in_flight=14-(7+4)+3=6, delta=1, sndcnt=min(1,1)=1, sndcnt=max(1,0)=1,因此cwnd=7,此时拥塞窗口允许发出一个数据包,接着server端快速重传No34数据包并更新prr_out=4,retrans_out=4。

    No35-No36:server收到No35后,sacked_out=8,fackets_out=13,lost_out=4,prr_delivered=8,in_flight=14-(8+4)+4=6, delta=1, sndcnt=min(1,1)=1, sndcnt=max(1,0)=1,因此cwnd=7,此时拥塞窗口允许发出一个数据包,此时server端被标记为lost的数据包已全部重传了,没有额外的数据包等待重传,因此server端发出新数据,对应No36,发出No36后packets_out=15,prr_out=5。

    No37-No38:No37的SACK新确认了一个数据包,更新sacked_out=9,fackets_out=14,此时fackets_out-dupthresh=11,而从Ack=101开始的第11个数据包为(601,651),这个数据包是没有被SACK确认的,因此TCP会把这个数据包标记为lost,进而更新lost_out=5。接着进行cwnd的更新操作,prr_delivered=9, in_flight=15-(9+5)+4=5, delta=2, sndcnt=min(2,1)=1, sndcnt=max(1,0)=1,因此cwnd=6,可以看到此时cwnd在Recovery状态下又降低到了ssthresh下面。此时cwnd允许发出一个数据包,因此发出No38重传包,更新retrans_out=5,prr_out=6。

    No39-No41:client收到No25重传包后,回复No39确认包,No39的SACK信息没有发生变化,但是ack number新确认了一个之前标记为lost的数据包,server收到No39后更新packets_out=14,fackets_out=13,lost_out=4,retrans_out=4,因为SACK块信息没有发生变化,因此sacked_out不变,此时in_flight=14-(9+4)+4=5,prr_delivered=10, delta=2>0,因为这个No39这个ACK报文确认了新数据,而且没有被RACK做标记,因此sndcnt = min(delta, max(prr_delivered - prr_out,newly_acked_sacked) + 1)=min(2,max(10-6,1)+1)=2,sndcnt=max(2,0)=2,cwnd=7,注意到了吧在Recovery状态下,cwnd也可能会向上调整的,此时拥塞窗口允许发出两个新的数据包,即对应No40和No41,并更新packets_out=16,prr_out=8。

    No42-No43:server端收到No42后,更新packets_out=15,fackets_out=12,lost_out=3,retrans_out=3,sacked_out不变仍为9。此时in_flight=15-(9+3)+3=6,prr_delivered=11,delta=1>0,sndcnt=min(1,max(11-8,1)+1)=1,snd=max(1,0)=1,cwnd=6+1=7。此时拥塞控制允许TCP发出一个数据包,即对应No43,然后更新packets_out=16,prr_out=9。

    No44-No45:这组数据包的处理与No42-No43相同,发出No45后,packets_out=16, fackets_out=11,lost_out=2,retrans_out=2, sacked_out=9, prr_delivered=12,cwnd=7,prr_out=10。

    No46-No47:client收到No34这个重传包后,之前由No8、No9、No10、No11四个数据包丢包形成hole得以填上,可以看到No46的因此少了一个(301,601)的SACK块,这个SACK块中共包含6个数据包,因此更新sacked_out=3。No46的Ack=601,相比No44新确认了7个数据包,因此更新packets_out=9, fackets_out=4,这7个数据包(251,301)是之前被标记为lost的数据包并进行了重传,因此更新lost_out=1,retrans_out=1。此时in_flight=9-(3+1)+1=6, prr_delivered=13, delta=1>0, sndcnt=min(1,max(13-10,1)+1)=1,cwnd=7,此时拥塞控制允许发出一个数据包即No47,然后更新packets_out=10,prr_out=11。

    No48-No49:No48这个dup ACK对应No36数据包,server收到No48后,更新sacked_out=4,fackets_out=5,in_flight=10-(4+1)+1=6, prr_delivered=14, delta=1>0, sndcnt=min(1,max(14-11,1)+1)=1,cwnd=7,此时拥塞控制允许发出一个数据包即No49,然后更新packets_out=11,prr_out=12。

    No50-No51:client端收到No38的重传后,最后一个由No19形成的hole也被填充上了,因此No50中不在带有SACK信息,更新sacked_out=0, fackets_out=0,No50的Ack=851新确认了5个数据包,更新packets_out=6,其中4个是之前被SACK确认的,1个是被标记为lost并进行了重传的,因此更新lost_out=0, retrans_out=0,注意No50的Ack=851>high_seq=801,因此此时server端的TCP进入Open状态,更新cwnd=ssthresh=7,即此时TCP处于reno的拥塞避免过程中,更新cwnd_cnt=5,此时in_flight=6,因此拥塞控制允许TCP发出一个数据包即No51,发出后更新packets_out=7。

    No52-No53:server端收到No52后更新packets_out=6,cwnd_cnt=6,cwnd=7,TCP发出No53数据包后更新packets_out=7。

    No54-No56:server收到No54后更新packets_out=6,cwnd_cnt=7,此时cwnd_cnt/cwnd=1,因此更新cwnd=8,cwnd_cnt=0。此时拥塞控制允许TCP发出两个数据包No55和No56,并更新packets_out=8。此时server端write写入的数据都已经发出去了。

    No57-No64:server端依次收到client端的ACK确认包,最终发出No64后packets_out=0, fackets_out=0,lost_out=0,retrans_out=0, sacked_out=0, cwnd=9(server端收到No59后reno的拥塞避免又更新cwnd=cwnd+1=9),ssthresh=7,cwnd_cnt=0。

    最后依然给出系列号的时序图,不过因为这个图中不能显示处SACK的信息,所以看起来不像SACK关闭场景下那么直观了

    补充说明:

    1、https://tools.ietf.org/html/draft-mathis-tcp-ratehalving-00

    2、https://tools.ietf.org/html/rfc6937

    3、http://conferences.sigcomm.org/sigcomm/1996/papers/mathis.pdf

    TCP系列47—拥塞控制—10、FACK下的快速恢复与PRR的更多相关文章

    1. TCP系列44—拥塞控制—7、SACK关闭的快速恢复

      ) return;    delta = ssthresh - in_flight;     prr_delivered += newly_acked_sacked; if (delta < 0 ...

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

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

    3. TCP系列41—拥塞控制—4、Linux中的慢启动和拥塞避免(一)

      一.Linux中的慢启动和拥塞避免 Linux中采用了Google论文的建议把IW初始化成了10了.在linux中一般有三种场景会触发慢启动过程 1.连接初始建立发送数据的时候,此时cwnd初始化为1 ...

    4. TCP系列52—拥塞控制—15、前向重传与RACK重传拥塞控制处理对比

      一.概述 这里主要简单分析一个丢包重传并恢复的场景,通过不同的设置让这个相同的场景分别触发RACK重传和前向重传,通过对比说明以下问题: Forward Retransmit可以产生只有重传标记的数据 ...

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

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

    6. TCP系列39—拥塞控制—2、拥塞相关算法及基础知识

      一.拥塞控制的相关算法 早期的TCP协议只有基于窗口的流控(flow control)机制而没有拥塞控制机制,因而易导致网络拥塞.1988年Jacobson针对TCP在网络拥塞控制方面的不足,提出了& ...

    7. TCP系列55—拥塞控制—18、其他拥塞控制算法及相关内容概述

      前面我们演示分析了100+个wireshark TCP实例,拥塞控制部分也介绍常见的拥塞处理场景以及4种拥塞撤销机制,但是我们一直使用的都是reno拥塞控制算法.实际上拥塞控制发展到今天已经有了各种各 ...

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

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

    9. TCP系列50—拥塞控制—13、Eifel探测下的拥塞撤销

      一.概述 我们之前在SACK关闭场景下的拥塞撤销那篇文章中提到过Eifel探测算法(Eifel Detection Algorithm),最早在介绍DSACK和FRTO的时候我们就有提到过Eifel探 ...

    随机推荐

    1. 使用树莓派拍摄延时动画,制作GIF图

      本期教大家将编写一个小脚本用树莓派来捕获多个图像,然后可以将这些图像组合成动画GIF,使用延时摄影功能,可以在几秒钟内查看非常慢的事情. 我们需要用到ImageMagick,这是一个用于图像处理的命令 ...

    2. vs code 写C心得

      用命令行的话可能比较简单: g++ -o [目标文件名] [原文件名] 然后在当前路径下直接执行这个文件,查看程序执行结果 例如: g++ -o a.out test.cpp ./a.out 默认是a ...

    3. Oracle入门第三天(下)——子查询

      一.子查询 1.子查询语法 SELECT select_list FROM table WHERE expr operator (SELECT select_list FROM table) 示例: ...

    4. 20155202 《Java程序设计》实验三(敏捷开发与XP实践)实验报告

      20155202 <Java程序设计>实验三(敏捷开发与XP实践)实验报告 代码托管 实验内容 XP基础 XP核心实践 相关工具 实验要求 1.没有Linux基础的同学建议先学习<L ...

    5. asp.net self host and urlacl(解决UnHandledException Message:拒绝访问的问题)

      命令提示符(管理员权限)需要添加的代码: netsh http add urlacl url=http://*:9999/ user=Everyone listen=yes 其中: url:代码中的u ...

    6. Caliburn.Micro - IResult and Coroutines

      IResult and Coroutines 翻译[三台]:网址[http://home.cnblogs.com/u/3Tai/] Previously, I mentioned that there ...

    7. OpenStack入门篇(一)之云计算的概念

      1.云计算 云计算是一种按使用量付费的模式,这种模式提供可用的.便捷的.按需的网络访问, 进入可配置的计算资源共享池(资源包括网络,服务器,存储,应用软件,服务),这些资源能够被快速提供,只需投入很少 ...

    8. Yii2 使用 faker 生成假数据

      测试过程中有时候需要生成大量的假数据,faker 是一个生成假数据的类库,可以生成姓名,电话,IP地址,密码,ISBN等等你能想到的或者你想不到的各种类型的假数据. Yii2.0已经集成该类库,不用再 ...

    9. Struts 2(四):类型转换

      类型转换是Struts 2的一个非常重要的部分,通过类型转换能够将表单参数转换成Java中的各种类型,本文将详细介绍Struts 2的内建类型转换器和自定义类型转换器. 第一节 Struts 2内建类 ...

    10. Html+CSS 学习第二天

      趁着这两天,将html和CSS基本上学了一遍,大家如果想学习的话,可以百度w3cSchool,进行学习. 基础我就不说了,直接将我做的一个登陆页面放上去.刚学完CSS,写个漂亮的登录界面恶心死我了,感 ...