TCP系列16—重传—六、基础快速重传(Fast Retransmit)

1、快速重传介绍linux

        按照TCP协议,RTO超时重传是一个很是重要的事件,当RTO超时的时候,TCP会同时经过两种方式很是谨慎的下降发送数据包的速率,一种是基于拥塞控制削减发送窗口的大小,另一个是经过指数回退增长每次RTO超时的时间(即karn算法的第二部分)。因此RTO超时后有可能会致使网络容量的利用不足。算法

        最开始咱们介绍tcp重传的时候就介绍过TCP还有另一种重传方式--快速重传。快速重传是RFC5681定一个的一个过程。快速重传不依赖定时器的超时,而是依靠ACK确认包来进行重传。使用快速重传相比RTO超时重传一般能够更高效的修复TCP丢包问题。快速重传是基于一个前提:即按照RFC5681,当TCP收到一个乱序报文的时候应该当即回复ACK确认包,而不会延迟ACK(延迟ACK介绍参考以前文章介绍)确认。另外RFC5681还指出若是接收序列号空间存在洞,新接收的报文彻底填充了这个洞或者部分填充了这个洞,TCP也应该当即回复一个ACK确认包以便发送端及时获取接收端相关的信息。缓存

        咱们举个例子假设有5个TCP报文,P1(1-10)、P2(11-20)、P3(21-30)、P4(31-40)、P5(41-50),其中括号中标注的是报文的比特系列号,每一个报文的长度都为10bytes。假设发送端依次发送这5个报文,其中P2报文在网络传输过程当中丢失,P一、P三、P四、P5报文依次按序到达,接收端收到这P1的时候发送ack=11的确认包(实际上这里可能会延迟发送ACK报文,为了描述简单咱们假设当即发送ACK报文),接收端收到P3的时候发现是乱序的报文则会当即回复ack=11确认包(还记得ACK是累计确认的吧,由于P2丢失了ACK只能累计到11),一样后面收到P4和P5的时候仍是会回复ack=11的确认包。这样发送端就会连续接收到4个ack=11的确认包,后面三个确认包由于和第一个ack number重复,由于称呼为duplicate ACK。所以接收端就能够依据dup ACK来推测接收端的接收状况。可是咱们以前说过IP层不会向TCP提供有序的数据报文,若是网络传输过程当中发生乱序致使接收端接收顺序变为P一、P三、P二、P四、P5,这样的状况下也会产生一个dup ACK。另外还有一种状况是IP层dup了ACK报文。咱们经过一个dup ACK并不能可靠的确认是发生了丢包仍是发生了乱序传输,所以会存在一个门限(duplicate ACK threshold或者叫作dupthresh),当TCP收到的dup ACK数超过这个门限的时候,就会认为发生了丢包,进而初始化一个快速重传。最初协议中给出的dupthresh这个门限是3,可是RFC 4653给出了一种调整dupthresh的方法。Linux中则能够经过/proc/sys/net/ipv4/tcp_reordering来设置默认值,另外Linux可能还会根据乱序测量的结果来更新实际的dupthresh。dupthresh的范围最终会在/proc/sys/net/ipv4/tcp_reordering和/proc/sys/net/ipv4/tcp_max_reordering之间。在没有使能SACK的时候,快速重传只会重传一个数据包,在使能SACK时候,SACK能够反映接收端是否存在系列号洞,进而容许发送端根据SACK的状况同时传输多个数据包。SACK的内容留到后面介绍。服务器

 

      最后补充一下window update的判断,通常若是一个TCP报文知足下面三个条件之一的话,linux就会认定这个报文是window update消息,被认定为window update的确认包是不会统计到dup ACK里面的,后面介绍窗口管理的时候还会进一步介绍一下window update。网络

一、ack number比以前接收的最大的ack number还要大socket

二、系列号seq比以前接收到的最大系列号还要大tcp

三、系列号seq与以前接收到的系列号相同,可是TCP头中的window size字段发生了变化测试

 

2、wireshark示例spa

  一、快速重传与RTO超时orm

设置/proc/sys/net/ipv4目录下tcp_retries2=8,tcp_early_retrans=0,tcp_sack=0,tcp_reordering=3,tcp_discard_on_port =9877(该参数为自加参数)。

  • client经过rawsocket与server创建链接,对应No1-No3报文

  • client先发送6bytes的数据,服务器回复ACK确认包,对应No4-No5报文

  • 服务器连续发送5个len=8的报文,对应No6-No10

  • client对No6报文回复ACK确认报文,对应No11

  • client丢弃No7报文来模拟传输过程当中丢包,并对No8-No10报文回复dupACK,对应No12-No14

  • server端在收到三个dup ACK后,认为No7报文已经丢失并当即触发快速重传,同时设置RTO超时定时器,对应No15

  • client对以后收到的报文直接丢弃不在回复ACK确认包

  • server端RTO超时后,继续重传对应的报文,并进行指数回退过程。最终屡次RTO超时重传失败后,server端释放TCP链接,对应No16-No21。

二、快速重传与window update消息

设置/proc/sys/net/ipv4目录下tcp_retries2=8,tcp_early_retrans=0,tcp_sack=0,tcp_reordering=3,tcp_discard_on_port =9877(该参数为自加参数)。

  • client经过rawsocket与server创建链接,对应No1-No3报文

  • client先发送6bytes的数据,服务器回复ACK确认包,对应No4-No5报文

  • 服务器连续发送5个len=8的报文,对应No6-No10

  • client丢弃No6报文来模拟传输过程当中丢包,并对No7-No10报文回复dupACK,对应No11-No14

  • 从wireshark抓包看,server端在收到四个dup ACK后,才认为No6报文已经丢失并当即触发快速重传,同时设置RTO超时定时器,对应No15

  • client对以后收到的报文直接丢弃不在回复ACK确认包

  • server端RTO超时后,继续重传对应的报文,并进行指数回退过程。最终屡次RTO超时重传失败后,server端释放TCP链接,对应No16-No21。

那么这里问题来了,咱们设置的tcp_reordering为3,也就是dupthresh值为3(实际上dupthresh能够动态调整,可是在此次测试中没有发生调整),那为何这里快速重传的触发须要四个dup ACK呢?缘由是这里No11对应的ACK报文,相比于No4的ACK报文虽然ack number都为3741168164,可是二者的Seq却发生了变化,所以linux认为No11是一个window update消息,而linux并不把window update消息计入dup ACK,后面收到的No12-No14报文才会被TCP认为是dup ACK,所以直到收到No14报文TCP才会认为dup ACK达到快速重传门限,触发快速重传。这里也反映了wireshark和linux在dup ACK认定上的差别性

三、快速重传与recovery point

       下面咱们在看一个快速重传后触发RTO超时重传而后收到ACK确认包的场景。这个测试过程与上一个示例相似,差别部分在于接收端在收到No18的重传报文以及以后的报文的时会回复一个ACK确认包。咱们能够看到在触发快速重传以前,正常传输的数据中最高系列号的下一个待发送系列号为No10报文对应的1751769800(即1751769792+8),这个系列号节点称呼为recovery point,而No19确认包中Ack=1751769768小于以前的1751769800,对于这种ACK确认报文,TCP称呼为partial ACK。在TCP收到parital ACK报文的时候则会当即触发快速重传,如No20、No2二、No2三、No25所示。截图中最后一个RST消息则是因为server端应用层没有读取server缓存中的数据(No4报文的数据)而直接close,所以会产生RST消息。实际的消息流中还有一个No29消息则是因为client使用raw socket编写的程序没有处理server端的RST消息,client关闭的时候简单的发送了一条RST消息来通知server。

       咱们同时注意到,在发送端收到No19一个ACK确认包的时候,只是发出去了一个重传包,而在收到No21确认包的时候,TCP却发出去了两个重传包。这种差别则是因为TCP的拥塞控制形成的,后面咱们讲到拥塞控制的时候在经过几个示例来介绍说明。

 

 

补充说明:

一、快速重传代码点tcp_fastretrans_alert,window update消息和dup ack的判断在tcp_ack_update_window和tcp_ack中

 

 

 

 

 

 

 

 

 

 



相关文章
相关标签/搜索