网络:TCP序列号和滑动窗口,顺序保证
TCP通过序列号(Sequence Number) 和滑动窗口(Sliding Window) 两大核心机制保证数据顺序,同时实现高吞吐量。以下从原理到实践逐步解析:
一、顺序保证的核心:序列号与确认号
1. 字节流编号
- 发送序列号(SEQ):每个TCP段携带的序列号 = 该段第一个字节在数据流中的全局位置
(如首段SEQ=1000,携带100字节 → 下一段SEQ=1100) - 确认号(ACK):接收方回复的ACK = 期望接收的下一个字节的序列号
(收到SEQ=1000的段后,回复ACK=1100)
2. 顺序控制流程
- 乱序处理:若段2(SEQ=1200)比段1先到达:
- 接收方发现SEQ=1200不连续(期望1000) → 缓存段2但不确认
- 收到段1(SEQ=1000)后 → 连续确认至1400(段1+段2)
✅ 关键点:接收方只按序确认连续数据,乱序数据暂存缓冲区。
二、滑动窗口:平衡顺序与吞吐量
1. 窗口定义
- 接收窗口(rwnd):接收方通告的剩余缓冲区大小(决定发送方可发多少数据)
- 拥塞窗口(cwnd):发送方根据网络拥塞估算的最大在途数据量
- 实际发送窗口 = min(rwnd, cwnd)
2. 滑动过程示例
- 初始状态:
- 发送窗口 = 3段(假设每段200字节)
- 发送:段1(1000-1199)、段2(1200-1399)、段3(1400-1599)
- 接收方确认段1:
- 窗口右移 → 可发送段4(1600-1799)
- 结果:
- 顺序性:段1确认前,段4不会发送
- 高吞吐:管道中始终有3个段在传输(并行)
3. 动态窗口调整
- 接收方控制(rwnd):
通过TCP头部的Window Size字段动态通告剩余缓冲区(如初始16KB → 降至8KB时限制发送速率)。 - 拥塞控制(cwnd):
- 慢启动:cwnd从1开始指数增长(1→2→4→8…)
- 拥塞避免:接近阈值时线性增长(+1/cwnd per ACK)
- 快速恢复:丢包时cwnd减半,避免连接重置
三、粘包/拆包与滑动窗口的关联
1. 粘包(Nagle算法)
- 问题:小数据包(如SSH按键)频繁发送 → 浪费带宽(40字节头+1字节数据)
- 解决:
- 发送方缓存小数据,直到满足:
a) 数据量 ≥ MSS
b) 收到前一个包的ACK - 副作用:延迟增加(如游戏操作卡顿)
- 禁用方法:setsockopt(TCP_NODELAY)
- 发送方缓存小数据,直到满足:
2. 拆包(MTU限制)
- 强制拆包:应用层数据 > MSS → TCP自动拆分为多个段
- 接收重组:接收方按SEQ号重组字节流,保证顺序透明
📌 关键结论:粘包是发送优化策略,拆包是传输层物理约束,二者均不影响接收端顺序。
四、如何追求更高吞吐量?
1. 窗口缩放(Window Scaling)
问题:TCP头部Window Size字段仅16位 → 最大窗口64KB(10Gbps链路下延迟>5ms即受限)
解决:握手时协商缩放因子(Scale Factor)
# Linux查看窗口缩放cat /proc/sys/net/ipv4/tcp_window_scaling # 默认1sysctl -w net.ipv4.tcp_window_scaling=3 # 缩放因子8(64KB*8=512KB)
2. 选择性确认(SACK)
问题:单个包丢失导致整个窗口重传(性能低下)
解决:接收方通告乱序接收的段范围
# SACK格式示例(TCP选项)TCP Header:Options: SACK=1200-1399, 1600-1799 # 收到段2和段4,但段3丢失
发送方仅重传段3(SEQ=1400-1599)
3. 高速网络优化
BBR算法:Google提出,通过测量带宽和延迟动态调整cwnd
# Linux启用BBRsysctl -w net.ipv4.tcp_congestion_control=bbr
效果:YouTube平均吞吐提升4%,延迟降20%
总结
1. 顺序保证:
- 序列号(SEQ)标记字节位置
- 接收方按序确认(ACK)
2. 吞吐优化:
- 滑动窗口控制管道数据量
- 动态调整(rwnd/cwnd)适配网络状态
3.高级机制:
- 窗口缩放突破64KB限制
- SACK减少重传开销
- BBR等算法智能避堵
滑动窗口的精髓在于:**以顺序性为前提,通过管道并行化最大化吞吐。**理解这一平衡艺术,是掌握TCP协议的关键所在。