TCP 收到乱序报文会怎么处理?接收端如何重组有序字节流?
简化版
TCP 接收端按序交付字节流。若收到乱序报文,通常会先放入接收缓冲区,累计 ACK 仍停在缺失字节处;等缺口报文到达后,再把连续区间一起交给应用层。乱序可能由网络多路径、路由变化、重传或调度导致。
详细版
假设接收端期望 Seq=1000:
收到 [1000,1199] -> Ack 1200,可交付
收到 [1400,1599] -> Ack 1200,缓存乱序段
收到 [1200,1399] -> Ack 1600,可交付 1200..1599
接收端要处理乱序、重复、重叠报文段,并保证最终交付给应用的是无重复、按顺序的字节流。
完整版教学
一、乱序报文为什么会出现
IP 网络只尽力而为,不保证路径和到达顺序完全稳定。不同报文可能经历不同队列、不同转发路径或不同重传时机。
发送顺序: A, B, C
到达顺序: A, C, B
TCP 必须在端系统里把乱序数据重新整理成有序字节流。
二、接收端的核心状态
接收端最关心的是“下一个期望字节序号”,也就是累计 ACK 的值。
| 事件 | 接收端动作 | Ack |
|---|---|---|
| 收到期望段 | 交付或推进连续区间 | 前进 |
| 收到后续乱序段 | 缓存 | 不前进 |
| 收到重复段 | 丢弃或合并 | 通常不前进 |
| 缺口补齐 | 合并连续区间 | 大幅前进 |
关键点:乱序数据可以先缓存,但累计确认不能越过缺口。
三、为什么不能直接交付乱序数据
TCP 给应用的是有序字节流。应用层通常没有能力也不应该处理缺洞。
应用读取文件内容
如果中间缺 200 字节,后面先给应用会破坏语义
所以 TCP 层必须等前面的缺失字节到齐。
四、重叠和重复报文怎么处理
重传可能导致接收端再次收到已接收范围的数据,也可能出现部分重叠。
已收 [1000,1499]
又收 [1200,1699]
新增部分只有 [1500,1699]
实现需要按序号范围合并,去掉重复字节,避免交付两次。
五、和快速重传的关系
乱序到达时,接收方会重复确认缺口位置。发送方收到多个重复 ACK 后,可以判断某段可能丢失,触发快速重传。
接收方反复 Ack 1200
发送方推测 1200 开始的数据丢了
如果支持 SACK,接收方还能告诉发送方哪些后续区间已经收到,帮助更精准重传。
六、接收缓冲区的压力
乱序数据占用接收缓冲区。如果缺口长期不补齐,后续数据越积越多,可能导致接收窗口变小,甚至影响发送方继续发送。
乱序缓存多
可用接收缓冲减少
通告窗口变小
发送方被限制
这也是弱网和丢包环境下吞吐下降的原因之一。
七、常见误区与追问
- 误区:TCP 不会收到乱序包。 IP 层不保证顺序,TCP 负责重组。
- 误区:乱序包到达后 Ack 会跳到最后。 累计 Ack 不能越过缺口。
- 误区:重复报文一定是错误。 重传和 ACK 丢失都可能导致重复,TCP 会去重。
- 追问:乱序数据会丢弃吗? 通常会缓存,受接收缓冲区和实现策略限制。
- 追问:SACK 有什么帮助? 告知发送方已收到的非连续区间,减少不必要重传。
- 追问:乱序会影响应用吗? TCP 会屏蔽乱序,应用只看到有序字节流,但可能感到延迟增加。
八、加强记忆
TCP 接收端像拼拼图:先来的后续块可以放旁边,但交给应用必须从缺口前连续交付。累计 ACK 永远指向第一个缺失字节;缺口一补齐,ACK 才能向前跳。