目录
ACK 确认机制
Seq & ACK 计算公式
需要注意的,在实际中,ACK Num 的增量为传输的数据字节数,而不是次数。通过这样的方式,确认 TCP Segment 的完整传输。
所以,Seq & ACK 计算公式如下:
ACK = Seq + 传递的字节数 + 1
Seq = ACK
- Sender Segment 丢失;
- Receiver ACK 丢失。
- RTO 过大,重传慢,效率差。
- RTO 过小,会出现没有丢包就重传的情况,造成网络拥塞。
- SRTT:计算平滑 RTT。
- DevRTR:计算平滑 RTT 与最新 RTT 的差值。
- 在 Linux 中,α = 0.125,β = 0.25, μ = 1,∂ = 4。
- Sender 发送了 4 个 segments,但第 1 个丢失了,而第 2-4 个成功发送。
- Receiver 接收到第 2-4 个时,发现乱序,随即返回 3 个相同的 ACK,表示已经收到了 3 个,但有 1 个丢失了。
- Sender 在连续收到 3 个相同的 ACK 后,认为前面有丢包,于是立即重传,而不等待 timeout。
- SACK 用于告知丢失,D-SACK 用于告知重复。
- SACK 应用于 Send 失败场景,D-SACK 应用于 ACK 失败或网络延迟场景。
重传机制
超时重传
超时重传(Timeout Retransmit),即:Sender 在发送数据时会设定一个 Timer(定时器),根据 timeout 超时配置,如果在这个时间内没有收到 Receiver 的 ACK,则会重新发送数据。
TCP 会在以下 2 种情况发生超时重传:
RTT(往返时延)
RTT(Round-Trip Time 往返时延),即:数据从网络一端传送到另一端所需的时间,也就是包的往返时间。
RTO(超时重传时间)
RTO (Retransmission,Timeout 超时重传时间),即:发送方进行 TCP 超时重传的时间。
设置 RTO 需要非常谨慎:
可见,RTO 的值应该略大于 RTT 的值,这可让重传机制更高效。
动态计算 RTT 和 RTO
由于网络的复杂情况,实际上 RTT 的值是动态变化的,所以 RTO 也需要动态调整。例如:Kernel TCP 协议会通过动态采样 RTT 的数值以及波动范围,然后进行加权平均,算出一个平滑 RTT 的值。
RFC6289 建议使用以下的公式计算 RTO。
再次超时重传时,TCP 的策略是加倍超时时间。因为两次超时,就说明网络环境差,不宜频繁反复发送。
快速重传
超时重传,是以 timeout 为驱动的,缺乏灵活。例如:在一个延迟高的网络中,Sender 发出 segment,Receiver 收到并返回 ACK,但由于延迟,Sender 误以为丢包了,从而频繁触发超时重传。
为了避免无休止的重传进一步损害网络,TCP 引入了快速重传(Fast Retransmit),是一种以数据为驱动的重传机制。
快速重传流程:
SACK 选择性确认机制
快速重传依旧有缺陷,因为 Sender 无法判断具体丢的是哪一个 segment,而会重传所有 segments,这显然是没有必要的。
为此,TCP 引入 SACK(Selective Acknowledgment,选择性确认)机制,通过在 TCP Header Options 字段追加 SACK 标签来实现的。Receiver 通过 SACK 将目前的 “缓存地图“(丢了哪一块) 告知 Sender,Sender 继而有选择性的进行重传。
如下图,在快速重传的基础上,Receiver 结合 SACK 告知 Sender 丢失的是第 3 个包,需要单独重传。
D-SACK
D-SACK(Duplicate SACK,重复 SACK),是 SACK 的变体,在 SACK 的基础上,Receiver 用于告知 Sender 有哪些数据是重复的。
区别于 SACK:
ACK 失败场景:Receiver 通过 D-SACK 告知 Sender,实际上 4000 之前的包都收到了,Sender 理解了可以从 4000 开始继续发送,而避免了重传。
网络延时场景:Receiver 通过 D-SACK 告知 Sender 前面触发快速重传的原因是网络延时,而非丢包。