网络3️⃣TCP-可靠性-重传机制
TCP 实现可靠传输,离不开序列号与确认应答。
- SEQ:Sequence Number
- ACK:Positive Acknowledgement
- 当发送端的数据到达接收方主机时,会返回一个确认应答消息,代表已收到消息。
- 如果数据包在传输过程中丢失,TCP 会采用重传机制。
- 超时重传
- 快速重传
- SACK
- D-SACK
1、超时重传
1.1、超时重传
Timeout Retransmission:发送数据时设定一个定时器。
超过指定时间未收到对方的
ACK
报文,就会重发该数据。
发生超时重传的场景:
-
数据包丢失:对方根本没收到数据包,更不可能发送 ACK。
-
确认应答丢失:对方发送的 ACK 丢失。
1.2、相关概念
1.2.1、往返时延 RTT
往返时延(Round-Trip Time):数据发送时刻到接收到确认时刻的差值。
i.e. 包的往返时间。
1.2.2、重传超时 RTO
重传超时(Retransmission Timeout):即超时重传时间
精确测量 RTO
值是非常重要的,可以使得重传机制更高效。
-
RTO 过大:重发慢,丢包很久才重发。👉效率低,性能差。
-
RTO 过小:重传快,还没丢就重发。👉增加网络拥堵,导致更多超时和重发。
Hint:RTO 值应略大于 RTT 值。
RTT 值经常变化,因此 RTO 是一个动态变化的值。
1.3、RTO 计算
Linux 中计算 RTO 的步骤
- 采样 RTT:TCP 采样 RTT 并进行加权平均,计算出一个平滑的、动态变化的 RTT 值。
- 采样 RTT 偏差(aka. 抖动):即 RTT 波动范围,避免 RTT 出现较大波动。
RFC6289 建议的 RTO 计算公式:
- SRTT:计算的平滑的 RTT 值。
- DevRTR:SRTT 与最新 RTT 的差值。
-
根据大量实验结果,Linux 中 α = 0.125,β = 0.25, μ = 1,∂ = 4。
-
超时重发时,会将下一次超时时间间隔设置为之前的 2 倍(避免在网络环境差的情况下频繁重发)。
1.3、不足
超时重传的不足:超时周期相对较长。
👇 快速重传机制,可解决超时重发的时间等待。
2、快速重传
快速重传(Fast Retransmit):不以时间为驱动,而是以数据驱动重传。
当连续收到三个相同的 ACK 报文时,重传丢失的报文段。
(此时还未到超时重传定时器的过期时间,因此效率更高)
2.1、工作流程
示例:发送方先后发出 5 份数据,但 Seq2 丢失。
-
Seq1:接收方收到 Seq1,回复 ACK2(代表下一个期望收到的报文 Seq=2)
-
Seq2:丢失
-
Seq3:接收方收到 Seq3,检查 Seq 不是期望收到的 2,仍回复 ACK2。
-
Seq4、Seq5:同理,接收方会回复 ACK2。
-
发送方收到 3 个相同的
ACK
报文(ACK2),重传 Seq2。
2.2、不足
快速重传解决了超时时间过长的问题,但仍存在问题。
无法明确重传哪些报文。
示例:发送方先后发出 5 份数据,但 Seq2 和 Seq3 丢失。
- Seq1:接收方回复 ACK2(代表下一个期望收到的报文 Seq=2)。
- Seq2 和 Seq3:丢失
- Seq4 和 Seq5:接收方检查 Seq 不是期望收到的 2,先后回复两次 ACK2。
重传哪些报文?
- 仅 Seq2:效率低。因为 Seq3 还需要经历 3 次重复的 ACK 才能触发重传。
- Seq2 后的所有:资源浪费。Seq4 和 Seq5 重复发送了。
👇
SACK
方法,可以选择性重传。
3、SACK
选择性确认(Selective Acknowledgment):
在 TCP 首部的选项字段里添加
SACK
。
3.1、SACK 方法
SACK 方法:使用
SACK
告知发送方已接收的数据。
- 发送方触发快速重传时,根据接收方的
SACK
信息来重传丢失的数据。 - 在 Linux 中通过
net.ipv4.tcp_sack
控制(Linux 2.4 后默认开启)。
示例
-
发送方收到了三次同样的 ACK 报文(ACK200),触发快速重发机制。
-
通过
SACK
信息发现200~299
数据丢失,则重发此 TCP 段。
3.2、D-SACK
Duplicate SACK:使用
SACK
告知发送方重复接收的数据。
- 发送方触发超时重传时,根据接收方的
SACK
信息来得知数据已被接收(没有丢失)。- 可能是 ACK 丢失,而不是数据丢失。
- 可能是数据遭遇网络延迟。
- 在 Linux 中通过
net.ipv4.tcp_dsack
参数控制(Linux 2.4 后默认开启)。
① ACK 丢失
场景:接收方收到数据了,但是 ACK 报文丢失
-
发送方:迟迟没有接收到 ACK 报文,触发超时重传(第一个数据包 3000~3499)。
-
接收方:发现数据重复接收,回复一个包含 SACK 的 ACK 报文。
- SACK=3000~3500:代表重传的这一段数据已接收(这里的 SACK 就是
D-SACK
)。 - ACK=4000:代表 4000 之前的数据已被接收。
- SACK=3000~3500:代表重传的这一段数据已接收(这里的 SACK 就是
-
发送方:得知数据没有丢失,是 ACK 丢失。
② 网络延迟
场景:发送方的某个包被网络延迟了,导致接收方没有发出相应的 ACK 报文。
(但其它包正常接收)
- 500~999:正常接收,ACK1000。
- 1000~1499:网络延迟,接收方不会 ACK。
- 1500~1999, 2000~2499, 2500~2999:
- ACK1000:代表期望收到 seq1000 的数据。
- SACK:代表已接收的数据(此处是
SACK
方法,标识已接收)。
- 发送方:连续收到 3 个相同的 ACK 报文,触发快速重传(重传后的报文已被接收)。
- 被延迟的数据(1000~1499):到达接收方,接收方回复一个包含 SACK 的 ACK 报文。
- SACK=1000~1500:代表重传的这一段数据已接收(此处是
D-SACK
,标识数据重复接收)。 - ACK=3000:代表 3000 之前的数据已被接收。
- SACK=1000~1500:代表重传的这一段数据已接收(此处是
对比:
- SACK 方法:通过
SACK
得知哪些数据已经被接收,从而确定要重传的数据。- D-SACK:通过
SACK
得知哪些数据已经被接收,从而得知数据已被接收(无需重传)。