第14章 TCP超时与重传:RTT与RTO概念;RTO计算的经典方法;RTO计算的标准方法;超时重传;快速重传;SACK;DSACK
TCP的2种重传机制:
1.基于RTO的超时重传
2.快速重传:当接收到失序报文时,接收端立即回复重复ACK,且不会被延时发送;发送端收到3次重复ACK后,立即发送时序的报文。(3次是因为无法判断该时序报文是真的失序,还是丢失;只有真的丢失,重传才有意义;失序其实影响不大)
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
linux关于重传的参数配置 ====================================================================================== TCP有2个阈值来决定是否重传同一个报文: [root@centos7 ~]# cat /proc/sys/net/ipv4/tcp_retries1 #3 #表示TCP愿意尝试重传数据的次数,FRC1122建议至少3次 [root@centos7 ~]# cat /proc/sys/net/ipv4/tcp_retries2 #15----15次,对应约为13-30分钟,根据具体连接的RTO而定 #表示TCP重传数据的超时时间,FRC1122建议至少100秒;实际实现中,linux设置15次,对应约为13-30分钟,根据具体连接的RTO而定 [root@centos7 ~]# cat /proc/sys/net/ipv4/tcp_syn_retries #6 #默认值5次,约180秒;一个新连接最多尝试6次 [root@centos7 ~]# cat /proc/sys/net/ipv4/tcp_synack_retries #2
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
RTT与RTO概念;RTT的测量; ======================================================================================= RTT(Round Trip Time):一个连接的往返时间,即数据发送时刻到接收到确认的时刻的差值; RTO(Retransmission Time Out):重传超时时间,即从数据发送时刻算起,超过这个时间便执行重传。 https://blog.csdn.net/whgtheone/article/details/80970292 TCP的超时重传之深入了解RTT与RTO -------------------------------------------------------------------------------------------- RTT的测量可以采用两种方法: https://blog.csdn.net/zhangskd/article/details/7196707 (1)TCP Timestamp选项-----这种方法相对比较准确 (2)重传队列中数据包的TCP控制块----接收报文时,还需要计算才能进行对比;如果cpu繁忙的话,那么结果就相差比较大了。。 重传的数据,是不会对相应ack进行RTT测试的,避免了重传的二义性
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
RTO计算的经典方法;RTO计算的标准方法;TCP时钟粒度;RTO、srtt、rttvar初始化;linux的RTT与RTO计算 ======================================================================================= 经典方法【RFC0793】 SRTT <- α*SRTT +(1 - α)*RTT 估算方法:指数加权移动平均、低通过滤器 原理:利用现存的 SRTT 值和最新测量到的 RTT 值取一个加权平均 α为平滑因子,推荐值为0.8-0.9 RTO = min(ubound, max(lbound, (SRTT)*β)) ubound是 RTO 的上边界,建议1分钟 lbound 为 RTO 的下边界,建议1秒钟 β 称为时延离散因子,推荐值为 1.3 ~ 2.0。 ###这个计算公式就是将 (SRTT)*β 的值作为 RTO,只不过另外限制了 RTO 的上下限。 经典方法的问题1:计时器无法适应RTT的大规模变动,特别是RTT突然急剧增大,因为新测量的RTT值权重较小,所以RTO增加缓慢,造成了不必要的重传(RTT急剧增加说明网络已经过载,此时不必要的重传加重了网络的负担) 经典方法的问题2:因为重传二义性,客户端不清楚真实情况如何,所以计算错误时,RTT值不是偏大就是偏小,进而影响了RTO的计算。而忽略重传报文的RTT计算,也会导致其他问题。 重传二义性(数据发生重传后收到ack,那么这个ack到底是对第一次发送报文的确认,还是对第二次发送报文的确认呢) --------------------------------------------------------------------------------------------- 标准方法【J88】【RFC6298】 srtt <- (1-g)*srtt + g*M #srtt其实就是经典方法的SRTT;M就是经典方法的RTT;g是权重值,g=1/8;#所以这个公式其实和经典方法的公式一样,只是换了皮。。。 rttvar <- (1-h)*rttvar + h*|M-srtt| #增量h为新平均偏差样本,h=1/4;//计算 SRTT 与真实值的差距(称之为绝对误差|Err|),同样用到加权平均 RTO = srtt + 4*rttvar #估算出来的新的 RTO,rttvar 的系数 4 是调参调出来的 ###当RTT(M)变化时,偏差的增量越大,RTO增长越快 #这个算法的整体思想:结合平均值(就是基本方法)和平均偏差来进行估算,一波玄学调参得到不错的效果。 RTO边界 【RFC6298】在上面公式的基础上,基于TCP时钟粒度,将最终公式优化如下: RTO = max(srtt + max(G,4*rttvar),1000) #RTO的下限为1000ms,即1s;上限值也可以设置,无默认值; #G为计时器粒度,一般为500ms,但是新的linux实现采用1ms --------------------------------------------------------------------------------------------- TCP时钟是一个变量,随着系统时钟做出更新,但不是一对一地同步更新; 粒度:TCP时钟一个“滴答”的时间长度 --------------------------------------------------------------------------------------------- 【RFC6298】RTO初始值= 1s 初始SYN报文段采用的RTO为3s srtt初始化:srtt <- M rttvar初始化:rttvar <- M/2 ------------------------------------------------------------------------------------------ linux的RTT与RTO计算---P469
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
基于计时器的重传;二进制指数退避 ------------------------------------------------------------------------------------------ 基于计时器的重传 每个数据包都有相应的计时器,一旦超过 RTO 而没有收到 ACK,就重发该数据包。没收到 ACK 的数据包都会存在重传缓冲区里,等到 ACK 后,就从缓冲区里删除。 首先明确一点,对 TCP 来说,超时重传是相当重要的事件(RTO 往往大于两倍的 RTT,超时往往意味着拥塞),一旦发生这种情况,TCP 不仅会重传对应数据段,还会降低当前的数据发送速率,因为TCP 会认为当前网络发生了拥塞。 ------------------------------------------------------------------------------------------ 二进制指数退避:每次重传间隔时间加倍 注意:第1次重传和第2次重传的间隔看起来不是严格的时间加倍,而后续的都是严格的时间加倍 原因:重传机制是基于时钟的,每500ms进行一次计时,所以是基于时钟间隔来进行重传的;但是发包并不是基于时钟,不是基于500ms的,所以第1次的重传往往看起来会小一点 TCP的重传将会伴随着发生2件事: 1.基于拥塞机制,减小发送窗口大小 2.每次重传,增大RTO的退避因子,即退避因子翻倍,初始为1,后续为2,4,8,...
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
快速重传 ------------------------------------------------------------------------------------------ 快速重传机制【RFC5681】基于接收端的反馈信息来引发重传,而非重传计时器超时。 当接收到失序报文时,接收端立即回复重复ACK,且不会被延时发送;发送端收到3次重复ACK后,立即发送时序的报文,而不需要等到计时器超时。(3次是因为无法判断该时序报文是真的失序,还是丢失;只有真的丢失,重传才有意义;失序其实影响不大) 但快速重传仍然没有解决一个问题:到底该重传多少个包?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
带选择确认的重传SACK ------------------------------------------------------------------------------------------ 带选择确认的重传 SACK(Selective Acknowledgment),简单来讲就是在快速重传的基础上,返回最近收到的报文段的序列号范围,这样客户端就知道,哪些数据包已经到达服务器了。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
伪重传(TCP Spurious Retransmission);DSACK 重复SACK ------------------------------------------------------------------------------------------ 伪重传,虚假重传(TCP Spurious Retransmission):tcp发送端进行了超时重传,而实际上tcp发送端已经接受到报文,并回复了ack,只不过因为该ack因为某些原因延迟到达,超出了RTO时间,导致发送端进行了超时重传 这可能会导致问题:触发发送端的快速重传。。。好像有点道理,但又是不常见的情况,因为触发这个问题需要的条件比较多。。。 DSACK,即重复SACK,这个机制是在 SACK 的基础上,额外携带信息,告知发送方有哪些数据包自己重复接收了。DSACK 的目的是帮助发送方判断,是否发生了包失序、ACK 丢失、包重复或伪重传。让 TCP 可以更好的做网络流控。
==============================================================================================================================
《网络排查案例课》12 | 重传的认识:重传到底是怎么回事?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
虚假重传;超时重传;快速重传 ================================================================================================================== 虚假重传 有可能也是超时重传的,例如本案例的虚假重传,wireshark认为client已经回复ack(搭车数据报文),所以认为是虚假重传,但本质上应该是server端没有接收到client的数据报文导致了超时重传 对于虚假重传,一般不用特别处理,集中关注超时重传和快速重传就好了 ---------------------------------------------------------------------------------------------- 超时重传,Timeout Retransmission,简称 RTO Timeout 其实是基于一个计时器,在报文发送出去后就开始计时,在时限内对方回复 ACK 的话,计时器就清零;而如果达到时限对方还没回复 ACK 的话,重传操作就被触发。 对于超时重传: TCP 对于每条连接都维护了一个超时计时器,当数据发送出去后一定时限内还没有收到确认,就认为是发生了超时,然后重传这部分数据。 RTO 的初始值是 1 秒(在发送 SYN 但未收到 SYN+ACK 阶段)。(RFC6298规定:在一条 TCP 连接刚刚开始,还没有收到任何回复的时候,这时的超时 RTO 为 1 秒。在更早以前的规范里,这个值是 3 秒。) 在连接成功建立后,Linux 会根据 RTT 的实际情况,动态计算出 RTO。实际场景中,RTO 为 200ms 出头最为常见。 RTO 有上限值和下限值,常见值分别为 2 分钟和 200ms。(这2个值不能修改) 注意:3次握手和正常传输阶段的RTO是不一样的,3次握手RTO为1秒,而其他时间RTO一般为200ms ---------------------------------------------------------------------------------------------- 快速重传 如果对端回复连续 3 个 DupAck 即重复确认,我就把序列号等于这个 ACK 号的包重传。 对于快速重传: 快速重传的触发条件是:收到 3 个或者 3 个以上的重复确认报文(DupAck)。 在快速重传中,SACK(选择性确认)也起到了避免一部分已经到达的数据被重传。不过,也由于 TCP 头部长度的限制,SACK 只能放置 4 个块,再多也不行了。 快速重传只要 3 个 DupAck 就可以触发,实际上我们还可能观察到远多于 3 个 DupAck 的情况,这也是正常现象。 Spurious 重传对 TCP 传输的影响比快速重传和超时重传小很多,总体来说是一种影响不大的重传。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
wireshark关于重传的提示 ================================================================================================================== New fragment overlaps old data (retransmission?)。这是说,这个报文跟前面的报文有重合。这里的“重合”如何理解呢?比如,前面的报文是字节 100 到 200,新的报文是字节 150 到 250,那么两者在 150 到 200 字节之间就是重合的。这也不是很大的问题。 This frame is a (suspected) fast retransmission:这是快速重传报文,这个信息一般都比较准确,很少有错的时候,用 suspected 这个词,更多地体现了 Wireshark 开发人员的谦虚和严谨。 This frame is a (suspected) retransmission:这里就是超时重传了,Wireshark 发现抓包文件中没有相关的 DupAck,就推断出这个是超时重传。 Duplicate ACK (#1):这里有 28 个重复确认报文 如果在专家信息里看到 New fragment overlaps old data (retransmission?),这意味着多个报文之间的数据有重叠,但一般不是严重的问题。 Wireshark 提示的 (suspected) fast retransmission 就是快速重传报文。 Wireshark 提示的 (suspected) retransmission 就是超时重传报文。 如果发现有数据报文和 DupAck 数量不对等的情况,可以看一下是否有 TSO 的存在。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
SACK ================================================================================================================== SACK是tcp option选项,接收端通过ack告诉发送端已经接收的连续报文数量,通过SACK告诉发送端后续已经接收到的另一段连续报文,但是由于中间有一段报文未收到,所以这些已经收到的报文无法进行处理,需要通过SACK的方式告知发送端 SACK避免了重传后续的所有报文,发送端只需要重传未到达的那一个报文即可 SACK 全称是 Selective Acknowlegement,中文叫“选择性确认” SACK 要能工作,还需要 SACK permitted 这个 TCP 扩展属性的支持。这个字段只有在 TCP 握手的 SYN 和 SYN+ACK 报文中出现,表示自己是否支持 SACK 特性。 受限于 TCP Option 长度,SACK 部分最多只能容纳 4 个块。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
超时重传案例;快速重传案例 ---------------------------------------------------------------- 超时重传案例 看起来client进行了8次重传,然后就继续发后续的seq 进行断开连接了;server侧则从始至终都没有收到那个被重传的报文,相应的ack也是旧的 ------------------------------------------------------------------- 快速重传案例 抓包分析: 知道了,为什么看起来没有发送报文,却一直有ack。是因为前面一直在发送报文呀。。。seq 39257发送到81317,所以接收端的接收窗口应该比较大,发送端可以一直发送;但是呢,处理比较慢,相应的ack滞后了。。 其实这个现象就是需要稍微有点延迟,同时接受窗口比较大,所以就出现现象:ack报文和该ack确认的数据报文中间间隔了很多其他报文 服务端从 32 号报文之后,发送的数据报文一共是 14 个,为啥客户端要回复的 DupAck 有 28 个呢? TSO,操作系统就可以把大于 MSS 的 TCP 段,比如 2 个 MSS 或更大尺寸的段,交给网卡驱动来处理,后者会利用其硬件芯片做分段工作,重新组装成新的符合 MTU 的报文后发送出去。 因为 tcpdump 在内核里靠近网卡这一侧,所以 tcpdump 抓取到的还是 TSO 处理之前的大报文,只有到达了网卡并且被 TSO 机制做了分段处理后,才变成小报文。我们抓包文件里看到 14 个报文,实际发送出去的是 28 个报文,也就触发了 28 次 DupAck。
==============================================================================================================================
《网络排查案例课》13 | 重传的再认识:没有任何丢包却也一直重传?
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
ack对某个报文的局部确认 ================================================================ ack确认号是确认到字节数的,并不是确认某个报文,所以一些特殊场景是会出现对某个报文的局部确认 关键点:确认号本身代表字节数,所以它是字节级别的,而不是报文级别。也就是说,确认号是精确到某个字节的,而不是某个报文。
![](https://images.cnblogs.com/OutliningIndicators/ContractedBlock.gif)
案例: 应用为什么变慢了? ---------------------------------------------------------------------------- 本次案例的根因为LB厂商的BUG,在数据包较大时,会触发bug,***会对报文进行部分确认***(这不是应该RS来确认吗?但抓包的现象确实如此) 正式因为对一个报文的部分确认,导致了发送端发送的连续数据都要重新发送,同时还需要对这些数据先进行重组,再发送(因为部分确认,导致部分确认的那个报文有948字节没有确认,而每次发送都是1460字节,所以需要进行重组) ***这个重组需要消耗大量的CPU资源,增加了耗时;时间都花费在了各种包的分拆、重组上面了。*** 同时对抓包中出现的大量的dup ack存疑,不清楚为什么这么多。因为发送端发送的报文数量远远小于dup ack 本次案例的重传包具有“规律性现象”。 如果是网络设备问题导致丢包,那么丢包会是随机现象,不太可能像这样有规律(70 个 DupAck 加一个快速重传,不断循环)。而往往规律的背后,一般潜藏着某种未知的机制。 实际上,在 Bug 修复之前,我们通过扩大 TCP receive buffer size,使得缓冲区足够大(你 HTTP POST 请求大,我缓冲区更大),也做到了对 Bug 的规避。 根据滑动窗口定义:左边界不会左移只会右移(推进),收到左边已确认的重复确认包会直接丢弃。