一、介绍
在前面介绍thin stream时候我们介绍过有两种场景下可能不会产生足够的dup ACK来触发快速重传,一种是游戏类响应交互式tcp传输,另外一种是传输受到拥塞控制的限制,只能发送少量TCP报文.针对这种场景提出了一个快速重传的改进算法即早期重传(early retransmit,简称ER)。按照RFC5827,ER有两种形式一种是基于字节的,一种是基于包的,基于包的ER精度要高于基于字节的ER,linux实现的是基于TCP包的ER,因此我们这里只介绍基于包的ER。
ER是在没有新数据可以发送的场景下降低快速重传dup ACK的门限,dup ACK是由乱序TCP报文触发的,但是发出的总数据包的个数少于4个的时候,就会因为没有足够的dup ACK而不能触发快速重传(假设默认dup ACK门限是3)。发送端在接收到ACK时候如果要触发快速重传必须同时满足下面两个条件
发出去的但是还没有收到ACK确认的TCP报文个数(假设为oseg)小于4
缓存中没有未发送数据或者发送窗口受限不能发送新数据,如果允许发送新数据的话就可以进一步触发dup ACK来达到门限了。
当满足上面两个条件时候,如果这个tcp连接未使能SACK的时候,用来触发ER的dup ACK门限必须降低为
ER_thresh = oseg - 1
当这个TCP连接支持SACK的时候,触发ER的条件则变为,(oseg-1)个TCP包已经被SACK确认。
二、linux实现
在linux实现上ER受到控制/proc/sys/net/ipv4/tcp_early_retrans控制,这个参数的描述如下
tcp_early_retrans - INTEGER
Enable Early Retransmit (ER), per RFC 5827. ER lowers the threshold
for triggering fast retransmit when the amount of outstanding data is
small and when no previously unsent data can be transmitted (such
that limited transmit could be used). Also controls the use of
Tail loss probe (TLP) that converts RTOs occurring due to tail
losses into fast recovery (draft-dukkipati-tcpm-tcp-loss-probe-01).
Possible values:
0 disables ER
1 enables ER
2 enables ER but delays fast recovery and fast retransmit
by a fourth of RTT. This mitigates connection falsely
recovers when network has a small degree of reordering
(less than 3 packets).
3 enables delayed ER and TLP.
4 enables TLP only.
Default: 3
其中TLP的内容我们后续介绍,暂时只关注前这个参数为0、1、2的场景,这个参数为0时候关闭ER,为1的时候使能ER,为2的时候使能ER,但是会延迟ER重传(延迟时间为RTT/4),这样可以在一定程度上减少网络的冗余重传。
从上面的描述中我们看到ER也是为了降低TCP的dup ACK门限,前面内容我们thin stream时候也介绍过一个参数tcp_thin_dupack也是用来降低dup ACK门限的,因此这个参数使能时候会与ER冲突,所以当tcp_thin_dupack使能的时候,不管tcp_early_retrans怎么设置都会关闭ER。linux同时还要求tcp_reordering为3的时候才会使能ER(默认值即为3)。
另外linux实现上还有一个点与协议有差异,上面我们介绍过TCP连接支持SACK的时候,触发ER的条件则变为,(oseg-1)个TCP包已经被SACK确认。协议使用的是MUST来描述这个条件,但是linux实现上只要有任意一个TCP包被SACK就会触发快速重传。linux的这个实现是基于在TLP的协议草案中提出的增强ER。
三、wireshark示例
前4个示例我们使用与之前thin stream类似的流量模型来与之前的示例作对比(注意截图中的RST消息产生的原因是server端缓存的TCP报文数据没有被应用层读取而直接close掉了,不用关注RST消息)
1、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1缓存中没有新数据待发送
这是示例与之前thin stream示例类似,不同之处在于第一次RTO超时发生了指数回退(原因是tcp_thin_linear_timeouts=0),RTO超时后server端写入两个TCP报文(No11和No12),两个报文发出后client模式丢失No11报文,回复一个dup ACK包含17-25的SACK信息。此时缓存中没有待发送的新数据,同时oseg = 2(No11和No12两个报文),SACK了No12报文,满足触发ER的条件。因此触发了No14的快速重传,从这里我们可以看到设置tcp_early_retrans=1,tcp_thin_dupack=0时候和之前单独设置tcp_thin_dupack=1的结果类似都会降低快速重传dup ACK的门限,因此在设置了tcp_thin_dupack=1的时候就不会触发ER重传了。
2、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1缓存中有新数据待发送
在设置tcp_thin_linear_timeouts=0后,从下图中我们看到与第一次RTO超时重传开始进行指数回退了(与之前的thin_stream对比)。接着server端写入三个数据包,但是因为拥塞控制暂时只能发出两个TCP报文(No11和No12),接着client回复了一个dup ACK带有17-25的SACK信息,即模拟No11包丢失。可以看到此时并没有触发ER,原因是上面写入了三个tcp报文只发送了两个TCP报文,缓存中还有新数据可以发送。
3、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=2缓存中没有新数据待发送
这个示例与第一个示例类似,不同的是这次设置tcp_early_retrans为2延迟触发ER重传。从server端程序中可以看到此时的RTT为0.44s,从下图可以看到ER重传的No14报文与No13报文之间的时间差大约也是0.11s,正好是RTT/4。其他地方与第一个示例一样了
4、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=0缓存中没有新数据待发送
接下来我们来看一下即没有使能ER也没有使能thin stream优化的场景,从下图可以看到在收到No13的dup ACK时候并没有触发快速重传,最终RTO超时重传
5、tcp_thin_dupack=0 tcp_thin_linear_timeouts=0 tcp_early_retrans=1缓存中没有新数据待发送
接下来我们看一下我们之前说的linux实现上对dup ACK门限的判断与协议的差异性。在下图中server端发出No6-No9四个TCP报文后,client端正常回退No6报文的ACK确认包。接着模拟No7报文丢失,对No8乱序报文响应No11确认包,其中包含17-25的SACK信息。可以看到测试oseg为3,按照协议需要SACK确认2个TCP数据包才会触发ER,但是我们看到只需要No11通过SACK确认一个TCP报文(No8报文)就会触发ER。其中No13是client对No9响应的ACK报文,包含17-33的SACK信息。
补充资料: