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:猜数字游戏 我们今天来来开始简单的项目实战. 第一个简单项目是猜数字游戏. 简单来说,系统给了 ...
随机推荐
- python打印九九乘法表
每种编程语言都可能会遇到编写“九九乘法表”的问题,用Python来处理也是很简单的,具体代码如下(基于Python3)): i = 1 while i <= 9: j = 1 while j & ...
- html 页面中的 base href 和 target
它只能应用于标记<head>与</head>之间 href:网页上的所有相对路径在链接时都将在前面加上基链接指向的地址. target:—设定文件显示的窗口,同a标记中的tar ...
- spark成长之路(1)spark究竟是什么?
今年6月毕业,来到公司前前后后各种事情折腾下来,8月中旬才入职.本以为终于可以静下心来研究技术了,但是又把我分配到了一个几乎不做技术的解决方案部门,导致现在写代码的时间都几乎没有了,所以只能在每天下班 ...
- Python基本语法元素
静态语言(C/C++.Java):脚本语言(python.JavaScript.PHP) IPO(Input.Process.Output) #:python中的注释符号:''' ''':多 ...
- django创建第一个子应用-3
在Web应用中,通常有一些业务功能模块是在不同的项目中都可以复用的,故在开发中通常将工程项目拆分为不同的子功能模块,各功能模块间可以保持相对的独立,在其他工程项目中需要用到某个特定功能模块时,可以将该 ...
- Centos配置网卡子接口
1.检查OS是否加载802.1q模块: 方法一: [root@rs2 ~]# modinfo 8021q 方法二: [root@rs2 ~]# modinfo -F filename 8021q 方法 ...
- AMD,CMD,CommonJs,UMD讲解
一.CommonJS CommonJS规范加载模块是同步的,只有加载完成,才能执行后面的操作 CommonJS规范中的module.exports和require 每个文件就是一个模块,有自己的作用域 ...
- Maven项目配置tomcat插件实现项目自动部署到远程服务器
1.tomcat配置 在tomcat目录中的conf目录下找到tomcat-users.xml配置文件,然后搜索tomcat-users,进行tomcat用户的角色和权限配置,如下: <tomc ...
- 在ubuntu trusty下安装python的rasterio库
就这些吧.. apt-get update -y apt-get install -y software-properties-common add-apt-repository ppa:ubuntu ...
- 你的APK安全吗?来WeTest免费测!
腾讯安全联合实验室就曾在<2018上半年互联网黑产研究报告>指出,移动端黑产规模宏大,恶意推广日均影响用户超过千万. 尤其在网络强相关的APP流行年代,当APP应用客户端上传与获取信息,大 ...