TCP系列16—重传—6、基础快速重传(Fast Retransmit)
一、快速重传介绍
按照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报文在网络传输过程中丢失,P1、P3、P4、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提供有序的数据报文,如果网络传输过程中发生乱序导致接收端接收顺序变为P1、P3、P2、P4、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。
1、ack number比之前接收的最大的ack number还要大
2、系列号seq比之前接收到的最大系列号还要大
3、系列号seq与之前接收到的系列号相同,但是TCP头中的window size字段发生了变化
二、wireshark示例
1、快速重传与RTO超时
设置/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。

2、快速重传与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认定上的差异性。
3、快速重传与recovery point
下面我们在看一个快速重传后触发RTO超时重传然后收到ACK确认包的场景。这个测试过程与上一个示例类似,差异部分在于接收端在收到No18的重传报文以及之后的报文的时会回复一个ACK确认包。我们可以看到在触发快速重传之前,正常传输的数据中最高系列号的下一个待发送系列号为No10报文对应的1751769800(即1751769792+8),这个系列号节点称呼为recovery point,而No19确认包中Ack=1751769768小于之前的1751769800,对于这种ACK确认报文,TCP称呼为partial ACK。在TCP收到parital ACK报文的时候则会立即触发快速重传,如No20、No22、No23、No25所示。截图中最后一个RST消息则是由于server端应用层没有读取server缓存中的数据(No4报文的数据)而直接close,因此会产生RST消息。实际的消息流中还有一个No29消息则是由于client使用raw socket编写的程序没有处理server端的RST消息,client关闭的时候简单的发送了一条RST消息来通知server。
我们同时注意到,在发送端收到No19一个ACK确认包的时候,只是发出去了一个重传包,而在收到No21确认包的时候,TCP却发出去了两个重传包。这种差异则是由于TCP的拥塞控制造成的,后面我们讲到拥塞控制的时候在通过几个示例来介绍说明。

补充说明:
1、快速重传代码点tcp_fastretrans_alert,window update消息和dup ack的判断在tcp_ack_update_window和tcp_ack中
TCP系列16—重传—6、基础快速重传(Fast Retransmit)的更多相关文章
- [易学易懂系列|rustlang语言|零基础|快速入门|(16)|代码组织与模块化]
[易学易懂系列|rustlang语言|零基础|快速入门|(16)|代码组织与模块化] 实用知识 代码组织与模块化 我们知道,在现代软件开发的过程中,代码组织和模块化是应对复杂性的一种方式. 今天我们来 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(9)|Control Flows流程控制]
[易学易懂系列|rustlang语言|零基础|快速入门|(9)] 有意思的基础知识 Control Flows 我们今天再来看看流程控制. 条件控制 if-else if -else: / Simpl ...
- [易学易懂系列|rustlang语言|零基础|快速入门|系列文章]
简单易懂的rustlang入门教程. [易学易懂系列|rustlang语言|零基础|快速入门|(1)|开篇] [易学易懂系列|rustlang语言|零基础|快速入门|(2)|VCCode配置] [易学 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具]
[易学易懂系列|rustlang语言|零基础|快速入门|(28)|实战5:实现BTC价格转换工具] 项目实战 实战5:实现BTC价格转换工具 今天我们来开发一个简单的BTC实时价格转换工具. 我们首先 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链]
[易学易懂系列|rustlang语言|零基础|快速入门|(27)|实战4:从零实现BTC区块链] 项目实战 实战4:从零实现BTC区块链 我们今天来开发我们的BTC区块链系统. 简单来说,从数据结构的 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)]
[易学易懂系列|rustlang语言|零基础|快速入门|(26)|实战3:Http服务器(多线程版本)] 项目实战 实战3:Http服务器 我们今天来进一步开发我们的Http服务器,用多线程实现. 我 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)]
[易学易懂系列|rustlang语言|零基础|快速入门|(25)|实战2:命令行工具minigrep(2)] 项目实战 实战2:命令行工具minigrep 我们继续开发我们的minigrep. 我们现 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)]
[易学易懂系列|rustlang语言|零基础|快速入门|(24)|实战2:命令行工具minigrep(1)] 项目实战 实战2:命令行工具minigrep 有了昨天的基础,我们今天来开始另一个稍微有点 ...
- [易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏]
[易学易懂系列|rustlang语言|零基础|快速入门|(23)|实战1:猜数字游戏] 项目实战 实战1:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...
随机推荐
- Spring Boot 多环境部署
再简单的应用系统,通常都有两个环境——开发环境和线上环境.大型的企业应用还会有更多的环境,比如测试环境.准线上环境.演示环境等.应用的版本也可能对应了多个环境,比如1.0版本的演示环境.2.0版本的演 ...
- mysql 几种搜索引擎的比较
mysql中常见的数据库引擎之间的比较 转载自 深入浅出mysql数据库 MySQL5.5以后默认使用InnoDB存储引擎,其中InnoDB和BDB提供事务安全表,其它存储引擎都是非事务安全表. 若 ...
- Opportunity Helper
using System; using Microsoft.Xrm.Sdk; using Microsoft.Crm.Sdk.Messages; public class OpportunityHel ...
- Spark运行模式_本地伪集群运行模式(单机模拟集群)
这种运行模式,和Local[N]很像,不同的是,它会在单机启动多个进程来模拟集群下的分布式场景,而不像Local[N]这种多个线程只能在一个进程下委屈求全的共享资源.通常也是用来验证开发出来的应用程序 ...
- MQTT的学习之Mosquitto安装&使用(1)
Mosquitto是一个实现了MQTT3.1协议的代理服务器,由MQTT协议创始人之一的Andy Stanford-Clark开发,它为我们提供了非常棒的轻量级数据交换的解决方案.本文的主旨在于记录M ...
- CSS基础part1
CSS 概述CSS 指层叠样式表 (Cascading Style Sheets),样式定义了如何显示 HTML文件中的标签元素,CSS是一种用来表现HTML(标准通用标记语言的一个应用)或XML(标 ...
- AIDL 进程间通信的一个小小的总结
需求 项目需要,将做好的项目作为一个服务提供给另一个公司.我们需要提供一个apk,所以设计到进程间交互,不得不了解一下AIDL了. 了解一下AIDL 之前准备面试的时候,或多或少的了解了一点AIDL, ...
- VINS(九)Ceres Solver优化(未完待续)
使用Ceres Solver库处理后端优化问题,首先系统的优化函数为
- hive中的优化问题
一.fetch抓取 fetch 抓取是指,hive中对某些情况的查询可以不必使用MapReduce计算.(1)把hive.fetch.task.conversion 设置成none,然后执行查询语句, ...
- (转)EDM邮件制作规范完整版
转载:http://www.maildesign.cn/archives/1380 在我们的日常工作中,经常需要发送邮件和我们的会员沟通.如注册确认.营销推广等.这些由站方发给会员的信件,往往纯文本格 ...