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

一、RACK概述

RACK(Recent ACKnowledgment)是一种新的基于时间的丢包探测算法,RACK的目的是取代传统的基于dupthresh门限的各种快速重传及其变种。前面介绍的各种基于dup ACK的快速重传算法及其变种通过修改dupthresh门限等手段,有些可以迅速的探测到丢包,有些可以精确的探测丢包,但是没有能同时达到迅速和精确两个目标的算法。

RACK基本思想:如果发送端收到的确认包中的SACK选项确认收到了一个数据包,那么在这个数据包之前发送的数据包要么是在传输过程中发生了乱序,要么是发生了丢包。RACK使用最近投递成功的数据包的发送时刻来推测在这个数据包之前传输的数据包是否已经过期(expired),RACK把这些过期的数据包标记为lost。RACK可以修复丢包而不用等一个比较长的RTO超时,RACK可以用于快速恢复也可以用于超时恢复(快速恢复和超时恢复我们会在拥塞控制阶段详细讲解),既可以利用初传的数据包探测丢包也可以利用重传的数据包探测丢包,而且可以探测初传丢包也可以探测重传丢包,因此RACK是一个适应多种场景的丢包恢复机制。

在现今的网络环境中乱序传输是一个比较常见的场景,使用dupthresh和dup ACK来做丢包探测的可靠性越来越低。同时因为传统的基于系列号空间的乱序度来探测丢包时,如果发生报文重传,初传报文和重传报文在系列号空间就会重叠。而RACK基于时间的乱序来探测丢包的时候,重传报文和初传报文在时间线上是不重叠的,因此RACK可以同时利用初传报文和重传报文来探测丢包。

RACK使用的需要三个条件:

1、TCP连接必须使用SACK选项

2、对于每个发送的数据包,发送端必须存储这个数据包的发送时间,时间精度至少要达到毫秒精度。如果连接的RTT小于1ms,那么微秒精度将会更有利于RACK探测丢包。

3、对于每个发送出去的数据包,发送端必须存储这个数据包是否已经重传过。


二、RACK算法描述

RACK目前还是一个实验算法,RACK需要使用到的几个状态变量:

Packet.xmit_ts:数据包上次传输所对应的时间,如果是重传也需要记录这个时间。发送端需要对每个数据包都记录这个时间,且时间精度至少是毫秒

RACK.xmit_ts:在所有被ack number或者SACK确认的数据包中,最近发送的数据包的Packet.xmit_ts

RACK.end_seq:上面用于记录RACK.xmit_ts的数据包的终止系列号

RACK.RTT:上面用于记录RACK.xmit_ts的数据包对应的RTT

RACK.reo_wnd:表示这个TCP连接的时间乱序度,这个变量的单位是时间。RACK使用这个变量来推测丢包

RACK.min_RTT:估计的这个连接的最小RTT

注意这些变量的粒度,每个数据包都有一个Packet.xmit_ts变量,每个TCP连接维护一组RACK.xmit_ts, RACK.RTT, RACK.reo_wnd和RACK.min_RTT变量

算法实现:

1、当传输一个新的数据包或者重传一个旧的数据包的时候,把当前时间记录在与这个数据包对应的Packet.xmit_ts变量中。

2、当接收到一个ACK的时候

Step2.1:根据测量到的RTT更新RACK.min_RTT。

发送端可以使用这个连接的全局最小RTT来维护RACK.min_RTT,也可以使用一个每发送窗口最小RTT的滤波值来维护RACK.min_RTT。

Step2.2:更新RACK.reo_wnd。RACK.reo_wnd默认值为1ms,当探测到包乱序的时候可以设置RACK.reo_wnd=RACK.min_RTT/4

Step2.3:更新RACK.xmit_ts、RACK.RTT 和 RACK.end_seq。

首先在这个ACK新确认的数据包(包括通过ack number和SACK确认的数据包)中排除下面两类重传数据包

1、如果这个数据包中的TSecr指示这个确认包并不是确认的重传数据包。这个实际上是Eifel探测算法,后面我们会进行详细介绍。

2、这个数据包上次重传时间距离当前时间小于RACK.min_rtt。这个也意味着这个数据包多半是虚假重传。

接着在剩余的新确认的数据包中找出最近发送的数据包的Packet.xmit_ts,如果Packet.xmit_ts比RACK.xmit_ts在时间上更靠后,那么更新RACK.xmit_ts = Packet.xmit_ts。如果RACK.xmit_ts发生了更新,那么更新RACK.RTT = Now() - RACK.xmit_ts,RACK.end_seq = Packet.end_seq。如果RACK.xmit_ts没有更新,那么退出针对这个确认包的RACK处理流程,不再执行下面的丢包探测过程。

Step2.4:丢包探测

对于每个还没有被SACK完全确认的数据包,如果在时间上RACK.xmit_ts比Packet.xmit_ts + RACK.reo_wnd更靠后,说明这个数据包已经超过预计的时间乱序度,标记这个数据包为lost状态。另外对于未被标记为lost的数据包,发送端可以等待下次收到ACK确认包的时候再次进行RACK标记处理,也可以设置一个"reordering settling"定时器,以待定时器超时的时候把这个数据包标记为lost。设置定时器的方法可以防止大量丢包或者应用层发送数据受限而造成RTO超时。定时器的超时时间协议给出的是timeout = Packet.xmit_ts + RACK.RTT + RACK.reo_wnd + 1。

这里值得一提的是,RACK功能可以很好的与TLP功能配合,因为RACK可以使用重传包来探测丢包,因此TLP其实可以发送第一个未被确认的数据包来进行丢包探测,这样就可以应用层传输时延。


三、linux实现简介

目前linux中RACK功能受到/proc/sys/net/ipv4/tcp_recovery参数控制,tcp_recovery是一个比特开关,其中0x1比特位控制是否使能RACK,该比特位有效的时候,则打开RACK功能,默认值为0x1,即linux中默认打开RACK功能。

另外就是linux并没有实现"reordering settling"定时器功能。linux中RACK其余处理基于与上面描述的一致了。

目前RFC草案中只说RACK可以在快速恢复和RTO超时恢复阶段使用,我们这里重点关注RACK丢包探测的机制,关于快速恢复和超时恢复,我们后面介绍拥塞控制的时候会在进行详细介绍,同时会再次回顾RACK的相关内容。


四、wireshark示例

我们先简单的看一个示例来理解RACK重传,后面介绍完拥塞控制的时候,会再次引入其他的RACK示例

1、RACK重传基础示例

在进行测试之前我们设置tcp_timestamps=0以关闭TCP的时间戳选项,用来说明RACK虽然是基于时间的丢包探测,但是并不依赖TSopt选项。当然这里打开这个TSopt这个选项也是可以的,只不过为了场景正交,因此这里演示关闭TSopt时候RACK一样可以正常处理。

业务场景:client与server端建立连接后,server端连续发送8个数据包,即No4-No11。其中只有No4和No11成功传输到接收端,中间的6个数据包(我手动设置了高亮显示)都丢失了。其中client与server端的RTT大约稳定在50ms左右。

No1-No3:通过三次握手建立连接,其中TSopt选项未协商成功

No4-No11:server端大约以3ms为间隔,连续发送了8个数据包,其中No5-No10高亮的这6个数据包丢失

No12:client回复对应No4的ACK确认包

No13:client回复对应No12的ACK确认包,通过SACK确认了No11,server端收到这个数据包的时候,更新fackets_out=7,此时dupthresh=3, fackets_out-dupthresh=4, 因此server端把No5、No6、No7、No8数据包标记为lost。

No14:server端先把No5数据包重传出去,对应No14,受限于拥塞控制,其余被标记为lost的数据包暂时还不能进行重传。

No15:No15通过ack number确认了No14这个重传包。server端更新 RACK.xmit_ts=No14的Packet.xmit_ts≈0.072604us,因为目前linux还没有检测到乱序传输(即dupthresh还没有更新),因此RACK.reo_wnd=1000us,此时server端会把RACK.xmit_ts-RACK.reo_wnd≈0.071604us时间点之前发送的数据包都标记为lost。即No9和No10也会被server端标记为lost。

No16-No29:接着在拥塞控制的限制下,对标记为lost的数据包进行重传操作,并在最终数据包传输完毕后关闭这个TCP连接。


补充说明:

1、https://datatracker.ietf.org/doc/draft-ietf-tcpm-rack/?include_text=1

2、https://www.ietf.org/proceedings/94/slides/slides-94-tcpm-6.pdf






posted @ 2016-11-07 13:56  lshs  阅读(3048)  评论(0编辑  收藏