Skip to content

Commit bc53e52

Browse files
committed
Merge branch 'main' of github.com:Snailclimb/JavaGuide
2 parents 8fd27cb + 719a969 commit bc53e52

1 file changed

Lines changed: 33 additions & 0 deletions

File tree

docs/cs-basics/network/tcp-reliability-guarantee.md

Lines changed: 33 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -111,6 +111,39 @@ Karn 算法的核心点是:对已经重传过的报文段,其 ACK 不用于
111111

112112
简单理解就是:RTO 不是 RTT,而是“平滑 RTT + 抖动余量”。如果一条连接的 RTT 样本大约是 100 ms,但抖动很大,RTO 就必须留出更大的安全余量;如果仍然超时,下一次 RTO 还会继续退避,避免在拥塞时把重传压力继续打到网络里。
113113

114+
## 快速重传是如何工作的?
115+
116+
超时重传可靠但偏慢,因为发送方必须等到 RTO 过期以后才会重传。快速重传(Fast Retransmit)解决的就是这个等待问题:它不依赖计时器,而是依赖接收方连续发回的重复 ACK 来更早发现疑似丢包。
117+
118+
TCP 使用累积确认。假设接收方已经按序收到了 `[0, 1000)` 这段字节,接下来期望收到从 1000 开始的数据。如果它先收到了 `[2000, 3000)`,说明中间 `[1000, 2000)` 这段还没到。接收方不会把 ACK 推进到 3000,而是继续回复 ACK = 1000,表示“我仍然在等 1000 之后的数据”。这种再次确认同一个旧 ACK 号的报文,就是 duplicate ACK。
119+
120+
发送方如果连续收到 3 个 duplicate ACK,通常会认为 ACK 指向的那段数据大概率已经丢失,于是不等 RTO 超时,直接重传缺失的数据段。之所以不是收到 1 个 duplicate ACK 就重传,是因为网络里可能出现短暂乱序:后发出的包先到,不一定代表前面的包真的丢了。3 个 duplicate ACK 是在“尽快恢复”和“避免误判乱序”之间做的折中。
121+
122+
快速重传只能更快定位“最早的缺口”。如果一个发送窗口里同时丢了多段数据,仅靠累积 ACK 仍然很难告诉发送方哪些区间已经到了、哪些区间还缺着,这就需要 SACK。
123+
124+
## SACK 是如何提升重传效率的?
125+
126+
SACK(Selective Acknowledgment,选择性确认)用来补足累积 ACK 的信息盲区。普通 ACK 只能表达“某个序号之前的数据都收到了”,但无法表达“后面的某些区间虽然乱序,也已经收到了”。SACK 会在 ACK 的 TCP 选项里携带已经收到的非连续字节区间,帮助发送方只重传真正缺失的部分。
127+
128+
SACK 需要在三次握手时通过 SACK-Permitted 选项协商。启用后,ACK 号本身仍然遵循累积确认规则,SACK 选项额外携带一个或多个 SACK block。每个 SACK block 由 Left Edge 和 Right Edge 组成,表示接收方已经收到的字节区间 `[Left Edge, Right Edge)`
129+
130+
举个例子:发送方连续发送 `[0, 1000)``[1000, 2000)``[2000, 3000)``[3000, 4000)`,其中 `[1000, 2000)` 丢失,但后面两段已经到达。接收方的累计 ACK 仍然只能停在 ACK = 1000,但它可以在 SACK 里报告已经收到 `[2000, 4000)`。发送方据此就知道 `[1000, 2000)` 需要重传,而 `[2000, 4000)` 不必重复发送。
131+
132+
TCP 选项长度有限。SACK 选项本身需要 2 字节,每个 SACK block 需要 8 字节,所以一个 TCP 段最多能携带 4 个 SACK block;如果同时携带时间戳等其他 TCP 选项,可用空间还会更少。也就是说,SACK 不能无限记录所有乱序区间,但它已经足以显著减少多段丢包时的无效重传。
133+
134+
## D-SACK 有什么作用?
135+
136+
D-SACK(Duplicate SACK,重复选择性确认)是对 SACK 的扩展,定义在 RFC 2883 中。SACK 主要告诉发送方“哪些非连续区间已经收到”,D-SACK 则进一步告诉发送方“哪些区间被重复收到了”。
137+
138+
D-SACK 不引入新的 TCP 选项,而是复用 SACK block。它约定:如果第一个 SACK block 描述的是一段已经被累计 ACK 覆盖的数据,或者描述的是一段已经出现在后续 SACK block 中的数据,那么这个 block 就是在报告重复数据。
139+
140+
为什么重复数据有价值?因为一次重传不一定代表原始数据真的丢了。常见情况包括:
141+
142+
- 原始数据已经到达接收方,但 ACK 在返回途中丢失,发送方等到 RTO 后误以为数据丢了,于是重传。
143+
- 原始数据在网络中严重乱序或延迟,发送方先触发了快速重传,随后原始数据和重传数据都到达接收方。
144+
145+
收到 D-SACK 后,发送方可以推断这次重传可能是误重传,并进一步判断网络中是否存在 ACK 丢失、严重乱序或 RTO 设置过小等问题。它不能单独证明某一种具体原因,但能为拥塞控制和排查重传异常提供重要线索。
146+
114147
## TCP 如何实现流量控制?
115148

116149
**TCP 利用滑动窗口实现流量控制。流量控制是为了控制发送方发送速率,保证接收方来得及接收。** 滑动窗口是 TCP 的核心机制之一,它既用于追踪“哪些数据已经发送但还没被 ACK”,也用于承载流量控制。接收方会在 ACK 报文中通过窗口字段通告自己的接收窗口(rwnd),发送方据此调整发送窗口。将窗口字段设置为 0,就表示接收方暂时没有可用缓冲区,发送方不能继续发送普通新数据。

0 commit comments

Comments
 (0)