当前位置: 首页 > news >正文

计算机网络-TCP的重传机制

内容来源:小林coding

本文是对小林coding的TPC重传机制的精简总结


超时重传

重传机制的其中一个方式,就是在发送数据时,设定一个定时器

当超过指定的时间后,没有收到对方的 ACK 确认应答报文,就会重发该数据,也就是我们常说的超时重传。

TCP 会在以下两种情况发生超时重传:

  • 数据包丢失
  • ACK确认应答丢失


超时重传的超时时间应该设置为多少?

什么是RTT(Rount-Trip Time 往返时延)

RTT 指的是数据发送时刻到接收到确认的时刻的差值,也就是包的往返时间
超时重传时间是以 RTO(Retransmission Timeout 超时重传时间)表示。
假设在重传的情况下,超时时间 RTO「较长或较短」时,会发生什么事情呢?


超时时间 RTO「较长或较短」时,会发生什么

上图中有两种超时时间不同的情况:

  • 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 当超时时间 RTO 较小时,会导致可能并没有丢失就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

精确的测量超时时间 RTO 的值是非常重要的,这可让我们的重传机制更高效。

根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值

至此,可能大家觉得超时重传时间 RTO 的值计算,也不是很复杂嘛。

好像就是在发送端发包时记下 t0,然后接收端再把这个 ack 回来时再记一个 t1,于是 RTT = t1 - t0。没那么简单,这只是一个采样,不能代表普遍情况

实际上「报文往返 RTT 的值」是经常变化的,因为我们的网络也是时常变化的。也就因为「报文往返 RTT 的值」是经常波动变化的,所以「超时重传时间 RTO 的值」应该是一个动态变化的值


Linux是如何计算RTO的

估计往返时间,通常需要采样以下两个:

  • 需要 TCP 通过采样 RTT 的时间,然后进行加权平均,算出一个平滑 RTT 的值,而且这个值还是要不断变化的,因为网络状况不断地变化。
  • 除了采样 RTT,还要采样 RTT 的波动范围,这样就避免如果 RTT 有一个大的波动的话,很难被发现的情况

其中 SRTT 是计算平滑的 RTT,DevRTR 是计算平滑的 RTT 与最新 RTT 的差距。

在 Linux 下,α = 0.125,β = 0.25,μ = 1,δ = 4。别问怎么来的,问就是大量实验中调出来的。

如果超时重发的数据,再次超时的时候,又需要重传的时候,TCP 的策略是超时间隔加倍

也就是每当遇到一次超时重传的时候,都会将下一次超时时间间隔设为先前值的两倍。两次超时,就说明网络环境差,不宜频繁反复发送

超时触发重传存在的问题是,超时周期可能相对较长。那是不是可以有更快的方式呢?

于是就可以用「快速重传」机制来解决超时重发的时间等待


快速重传机制

快速重传(收到三个ACK)

TCP 还有另外一种快速重传(Fast Retransmit)机制

它不以时间为驱动,而是以数据驱动重传
快速重传机制,是如何工作的呢?其实很简单,一图胜千言

在上图,发送方发出了 1,2,3,4,5 份数据:

  • 第一份 Seq1 先送到了,于是就 Ack 回 2;
  • 结果 Seq2 因为某些原因没收到,Seq3 到达了,于是还是 Ack 回 2
  • 后面的 Seq4 和 Seq5 都到了,但还是 Ack 回 2因为 Seq2 还是没有收到
  • 发送端收到了三个 Ack = 2 的确认,知道了 Seq2 还没有收到,就会在定时器过期之前,重传丢失的 Seq2
  • 最后,收到了 Seq2,此时因为 Seq3,Seq4,Seq5 都收到了,于是 Ack 回 6。

所以,快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段

快速重传机制只解决了一个问题,就是超时时间的问题


重传个数问题

但是它依然面临着另外一个问题

就是重传的时候,是重传一个,还是重传所有的问题

举个例子,假设发送方发了 6 个数据,编号的顺序是 Seq1 ~ Seq6,但是 Seq2、Seq3 都丢失了

那么接收方在收到 Seq4、Seq5、Seq6 时,都是回复 ACK2 给发送方

但是发送方并不清楚这连续的 ACK2 是接收方收到哪个报文而回复的

那是选择重传 Seq2 一个报文,还是重传 Seq2 之后已发送的所有报文呢(Seq2、Seq3、Seq4、Seq5、Seq6)呢?

  • 如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。
  • 如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5、Seq6 的报文是已经被接收过了,对于重传 Seq4 ~ Seq6 折部分数据相当于做了一次无用功,浪费资源

可以看到,不管是重传一个报文,还是重传已发送的报文,都存在问题。

为了解决不知道该重传哪些 TCP 报文,于是就有 SACK 方法


SACK方法(选择性确认) 

还有一种实现重传机制的方式叫:SACK(Selective Acknowledgment),选择性确认。

这种方式需要在 TCP 头部「选项」字段里加一个 SACK 的东西

它可以将已收到的数据的信息发送给「发送方」

这样发送方就可以知道哪些数据收到了,哪些数据没收到

知道了这些信息,就可以只重传丢失的数据
 

如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制

通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复

如果要支持 SACK,必须双方都要支持。在 Linux 下,可以通过 net.ipv4.tcp_sack 参数打开这个功能(Linux 2.4 后默认打开)


D-SACK 

Duplicate SACK 又称 D-SACK

其主要使用了 SACK 来告诉「发送方」有哪些数据被重复接收
下面举例两个栗子,来说明 D-SACK 的作用


ACK丢包

接收方表示这个数据收到了,但发送方表示没有收到ACK确认,说明这个数据是ACK丢包了

  • 「接收方」发给「发送方」的两个 ACK 确认应答都丢失了,所以发送方超时时,重传第一个数据包 (3000 ~ 3499)
  • 于是「接收方」发现数据是重复收到的,于是回了一个 SACK = 3000~3500,告诉「发送方」3000~3500 的数据早已被接收了,因为 ACK 都到了 4000 了,已经意味着 4000 之前的所有数据都已收到,所以这个 SACK 就代表着 D-SACK
  • 这样「发送方」就知道了,数据没有丢,是「接收方」的 ACK 确认报文丢了

网络延时

接收方表示这个数据收到了,发送方表示这个数据已经ACK确认过了,说明这个数据是网络延时丢包了

  • 数据包(1000~1499)被网络延迟了,导致「发送方」没有收到 Ack 1500 的确认报文
  • 而后面报文到达的三个相同的 ACK 确认报文,就触发了快速重传机制,但是在重传后,被延迟的数据包(1000~1499)又到了「接收方」
  • 所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包
  • 这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了

可见,D-SACK 有这么几个好处:

  1. 可以让「发送方」知道,是发出去的包丢了,还是接收方回应的 ACK 包丢了
  2. 可以知道是不是「发送方」的数据包被网络延迟了
  3. 可以知道网络中是不是把「发送方」的数据包给复制了

在 Linux 下可以通过 net.ipv4.tcp_dsack 参数开启 / 关闭这个功能(Linux 2.4 后默认打开)


 简单总结一下重传机制

什么是时候会触发超时重传

我们【数据包丢失】和【响应包丢失】的时候我们会触发我们的超时重传


超时重传时间设置

超时重传的超时时间设置:

  • 当超时时间 RTO 较大时,重发就慢,丢了老半天才重发,没有效率,性能差;
  • 当超时时间 RTO 较小时,会导致可能并没有丢失就重发,于是重发的就快,会增加网络拥塞,导致更多的超时,更多的超时导致更多的重发。

精确的测量超时时间 RTO 的值是非常重要的,这可让我们的重传机制更高效。

根据上述的两种情况,我们可以得知,超时重传时间 RTO 的值应该略大于报文往返 RTT 的值


快速重传机制

所以,快速重传的工作方式是当收到三个相同的 ACK 报文时,会在定时器过期之前,重传丢失的报文段

快速重传机制只解决了一个问题,就是超时时间的问题

还有一个问题,就是重传的时候,是重传一个,还是重传所有的问题

举个例子,假设发送方发了 6 个数据,编号的顺序是 Seq1 ~ Seq6,但是 Seq2、Seq3 都丢失了

那么接收方在收到 Seq4、Seq5、Seq6 时,都是回复 ACK2 给发送方

但是发送方并不清楚这连续的 ACK2 是接收方收到哪个报文而回复的,那是选择重传 Seq2 一个报文,还是重传 Seq2 之后已发送的所有报文呢(Seq2、Seq3、Seq4、Seq5、Seq6)呢?

  • 如果只选择重传 Seq2 一个报文,那么重传的效率很低。因为对于丢失的 Seq3 报文,还得在后续收到三个重复的 ACK3 才能触发重传。
  • 如果选择重传 Seq2 之后已发送的所有报文,虽然能同时重传已丢失的 Seq2 和 Seq3 报文,但是 Seq4、Seq5、Seq6 的报文是已经被接收过了,对于重传 Seq4 ~ Seq6 折部分数据相当于做了一次无用功,浪费资源

所以这两种方法都有问题

所以我们要有个东西去标记哪些数据被接收了,哪些数据没被接收


选择性确认-SACK方法

在 TCP 头部「选项」字段里加一个 SACK 的东西,它可以将已收到的数据的信息发送给「发送方」

这样发送方就可以知道哪些数据收到了,哪些数据没收到

知道了这些信息,就可以只重传丢失的数据

如下图,发送方收到了三次同样的 ACK 确认报文,于是就会触发快速重发机制

通过 SACK 信息发现只有 200~299 这段数据丢失,则重发时,就只选择了这个 TCP 段进行重复


哪些数据被重复接收了:D-SACK

我们有两种情况会重复接收数据

【ACK丢包】

【网络延时】


【ACK丢包】

接收方表示这个数据收到了,但发送方表示没有收到ACK确认,说明这个数据是ACK丢包了

因为我们的ACK都到4000了,说明4000之前的数据我们都已经接收到了

这时候我们发送过去的SACK就代表D-SACK

我们要发一个3000-3500的D-SACK,告诉【发送方】数据没有丢,是「接收方」的 ACK 确认报文丢了


【网络延时】

接收方表示这个数据收到了,发送方表示这个数据已经ACK确认过了,说明这个数据是网络延时丢包了

我们超时重传后,成功收到了这批数据

但是这批数据因为网络波动又传了过来

所以「接收方」回了一个 SACK=1000~1500,因为 ACK 已经到了 3000,所以这个 SACK 是 D-SACK,表示收到了重复的包

这样发送方就知道快速重传触发的原因不是发出去的包丢了,也不是因为回应的 ACK 包丢了,而是因为网络延迟了


区分SACK的D-SACK

SACK:知道哪些数据接收方没收到,从而重传丢失的数据

DSACK:发送方没接收到接收的ACK的时候,我们防止重传已经接收的数据,我们会返回最新的ACK过去,告诉他们我们这些数据我们已经接受过

http://www.dtcms.com/a/109928.html

相关文章:

  • 清晰易懂的 Flutter 开发环境搭建教程
  • java短连接,长连接
  • Linux命令-uniq
  • RAGFlow部署与使用介绍-深度文档理解和检索增强生成
  • 本地部署 Firecrawl 爬虫让 AI 知识库更丰满
  • Java创建对象和spring创建对象的过程和区别
  • AI赋能数据库管理“最后一公里”,融合架构重塑数据库承载成本效能——zCloud 6.7与zData X 3.3正式发布
  • MonkeyDev 如何创建一个root级级别的app,并执行root命令获取iphone设备序列号serialNumber(ios15.8)
  • 航电系统之承重与避障技术
  • “二分查找 + (必要时)前缀和” -- 处理 ’有序数组‘ 的区间问题汇总
  • 信息学奥赛一本通 1524:旅游航道
  • 胶铁一体化产品介绍
  • 什么是 SAML身份验证
  • 【DY】信息化集成化信号采集与处理系统;生物信号采集处理系统一体机
  • Qt实现登录界面(输入密码后过几秒,密码变为小黑点,眼睛改变密码明文,密文)
  • 电子电气架构 --- SOC设计流程及其集成开发环境
  • 企业知识库如何搭建?从零开始构建高效知识管理体系
  • 【Spring】Spring 注解解码:@RequestBody 与 @ResponseBody 的双向桥梁艺术
  • 2024年B会(Coling)——双重对比学习用于多模态对话情感识别
  • 数字人训练数据修正和查看 不需要GPU也能运行的DH_live-加载自己训练-
  • 注意力机制在大语言模型中的原理与实现总结
  • 【算法中的数学】分解质因数
  • 每天学一个 Linux 命令(11):cp
  • 【系统移植】(六)第三方驱动移植
  • MySQL:数据类型
  • 380_C++_[结合379]从连续内存地址中取出来的热力图图片data,转换为可视化的、带颜色的热力图像显示到界面(图像格式为RGBA)
  • Yapi部署指南:在 Linux 上 Yapi 教程
  • Linux Bash 脚本实战:自动监控域名证书过期并发送邮件告警
  • vue和angular实现飞机大战
  • 彩虹表攻击