TCP 拥塞控制与四次挥手解析

发布于:2025-09-09 ⋅ 阅读:(18) ⋅ 点赞:(0)

目录

  • 拥塞窗口 cwnd 与接收窗口 rwnd
  • 如何判断出现了拥塞
  • 四大拥塞控制算法(慢启动/拥塞避免/快重传/快恢复)
  • TCP 连接释放:四次挥手(含 2MSL 详解)
  • 粘包与拆包:原因、Nagle、延迟 ACK 与工程级解决方案
  • TCP Push 标志:何时“立即发送”
  • TCP 与 UDP 的差异与选型
  • 高频面试题汇总
  • 总结

在这里插入图片描述

拥塞窗口 cwnd 与接收窗口 rwnd

  • 发送端维护 cwnd(拥塞窗口),接收端通告 rwnd(接收窗口/流量控制窗口)。
  • 实际可发送窗口 swnd = min(cwnd, rwnd),即受拥塞控制与流量控制的共同限制。
  • cwnd 是发送方根据网络拥塞状态动态调整的变量:
    • 网络良好(无丢包、无超时)时逐步增大。
    • 出现拥塞(丢包、超时等)时减小,以缓解网络压力。

关键现象与直觉

  • 只要网络中没有出现明显拥塞,cwnd 会“试探性”增大;
  • 一旦触发拥塞信号(超时/重复 ACK),cwnd 迅速降低(乘性减小),并进入相应阶段(如快速恢复)。

如何判断出现了拥塞

常见判定依据:

  • 超时重传(RTO 触发):最强烈的拥塞信号,一般认为网络拥塞严重。
  • 重复 ACK(DupACK):出现 3 个重复 ACK(即连续 3 次收到相同 ACK),多判定为个别报文段丢失,触发“快速重传”。

面试要点:重复 ACK ≠ 超时。重复 ACK 更温和,通常只丢了个别分段;超时表示整体更严重,拥塞窗口调整也更激烈。


四大拥塞控制算法

1)慢启动 Slow Start

  • 初始 cwnd 较小(通常按 MSS 的倍数)。
  • 每收到一个 ACK,cwnd 线性增加;按 RTT 计算呈指数增长(每个 RTT 翻倍)。
  • cwnd 达到慢启动阈值 ssthresh 时,转入拥塞避免。

2)拥塞避免 Congestion Avoidance

  • 进入该阶段后,增长速度从“指数”变为“加法增大”(每个 RTT 大约 +1 MSS)。
  • 目的:在较大窗口下小心试探,避免过快推高引发拥塞。

3)快速重传 Fast Retransmit

  • 触发条件:收到 3 个重复 ACK。
  • 动作:无需等到 RTO,立即重传“推测丢失”的报文段。

4)快速恢复 Fast Recovery

  • 典型策略(Reno 思想):
    • ssthresh = cwnd / 2(乘法减小)。
    • cwnd 设为 ssthresh + 3*MSS(对已收到的 3 个重复 ACK 进行“拥塞窗口补偿”)。
    • 之后收到新的重复 ACK,cwnd +1 MSS;当收到新数据的 ACK,cwnd 设为 ssthresh,进入拥塞避免。

小结:

  • 超时 -> 认为更严重 -> 直接退回慢启动(cwnd 置 1 或较小)。
  • 3 次重复 ACK -> 快速重传 + 快速恢复(温和收缩)。

TCP 连接释放:四次挥手(含 2MSL)

四步动作(典型场景:客户端主动关闭):

  1. 客户端发送 FIN=1,进入 FIN_WAIT_1
  2. 服务器回 ACK,客户端进入 FIN_WAIT_2(半关闭,客户端不再发数据,可收数据);
  3. 服务器发送 FIN=1,进入 LAST_ACK
  4. 客户端回 ACK,进入 TIME_WAIT,等待 2MSL 后彻底关闭;服务器收到 ACK 后立即关闭。

为什么需要 2MSL?

  • 确保最后一个 ACK 能到达;若丢失,服务器会重发 FIN,客户端在 TIME_WAIT 仍能重发 ACK。
  • 让网络中的旧报文段(可能延迟)在 2MSL 窗口内自然消失,避免“旧连接”报文污染“新连接”。

能否缩短为 2/3 次?

  • 技术上存在“延迟 ACK 与 FIN/ACK 合并”的情况,使得“看起来”不像严格 4 次,但语义上仍遵守四步。
  • 生产上慎做“强制合并”,更应该遵循协议与栈的既定实现。

粘包与拆包:根因与工程实践

结论先行:TCP 是“面向字节流”的协议,没有消息边界;粘包/拆包是应用层需要处理的“消息定界”问题。

为什么会粘包/拆包?

  • Nagle 算法:为减少小包,提高链路利用率,会把多个小报文合并发送(粘包)。
  • 延迟 ACK:接收端为了可能的合并确认,会短暂等待并合并确认,使发送方报文聚合。
  • 应用层一次 write 与底层一次发送并非一一对应;
  • 接收端一次 read 也可能读到多个/半个消息。

工程级解决方案

  • 禁用 Nagle(TCP_NODELAY)+ 视业务开启/关闭延迟 ACK(平台相关);
  • 应用层“定界协议”:
    • 定长消息(固定字节数);
    • 分隔符(如 \r\n);
    • TLV/前置长度字段(推荐):[length][payload]
    • 带帧头校验/序号等,便于容错与重组。

示例:前置长度帧的简化示意

| 4 bytes length | payload (length bytes) |

TCP Push 标志:让数据“立即传递”的信号

  • PSH(Push)是发送端对接收端的提示:“请尽快把数据推送到应用层,不要继续缓存”。
  • 常见于交互式/低延迟场景,如在线语音/视频的关键控制、金融撮合、即时消息指令等。
  • 注意:PSH 并非绕过拥塞控制/流控,只是传递“尽快递交”的语义。

TCP 与 UDP 的差异与选型

  • 传输语义:TCP 面向连接、可靠、有序;UDP 无连接、尽力而为、可能乱序/丢包。
  • 开销与性能:TCP 可靠性机制增大头部与状态开销;UDP 简洁、低时延、抖动小。
  • 适用场景:
    • TCP:Web/数据库/文件传输等“可靠性优先”的场景。
    • UDP:实时音视频、直播、在线游戏等“时延/抖动敏感”的场景(上层配合 FEC/自研可靠性)。

高频面试题汇总(快速复习)

  1. 为什么是四次挥手?能 2/3 次吗?
    • FIN 与 ACK 分属独立方向,需分别确认;特定实现中可能合并报文,但语义上仍是四步。
  2. 2MSL 的意义?
    • 保障最后 ACK 可达 + 清除旧报文影响新连接。
  3. 3 次重复 ACK 与超时分别触发什么?
    • 3 次重复 ACK:快速重传 + 快速恢复;超时:回到慢启动,窗口剧烈收缩。
  4. 如何从根上解决粘包拆包?
    • 应用层定界协议(前置长度/TLV/分隔符/定长)+ 视需求关闭 Nagle/调节延迟 ACK。
  5. swnd = min(cwnd, rwnd) 的直觉?
    • 既要遵守接收端能力(rwnd),又要遵守网络拥塞状况(cwnd)。

总结与参考

  • 把握三条主线:
    1. 拥塞控制(cwnd/ssthresh + 四大算法)。
    2. 连接终止(四次挥手 + 2MSL)。
    3. 应用工程(粘包/拆包 + Push + TCP/UDP 选型)。

网站公告

今日签到

点亮在社区的每一天
去签到