网络与通信

TCP 面试手册

覆盖 TCP 三次握手、四次挥手、确认重传、流量控制、拥塞控制和 TIME-WAIT 等面试高频考点。

难度:进阶
文章目录

知识地图

graph TD
    A[TCP 协议] --> B[连接管理]
    A --> C[可靠传输]
    A --> D[性能机制]
    B --> B1[三次握手]
    B --> B2[四次挥手]
    B --> B3[TIME-WAIT]
    B --> B4[SYN Flood]
    C --> C1[序列号与确认]
    C --> C2[超时重传]
    C --> C3[快速重传]
    D --> D1[滑动窗口]
    D --> D2[拥塞控制]
    D --> D3[Nagle 算法]

核心速记卡

概念一句话定义关键限制
三次握手SYN → SYN+ACK → ACK,同步双方初始序列号防止旧重复连接请求建立无效连接
序列号每个字节有唯一编号,用于有序交付和去重初始序列号应随机化防攻击
滑动窗口接收方通告可接收的字节数,发送方据此限速窗口为 0 时发送方停止发数据
拥塞控制发送方根据拥塞信号动态调整发送速率具体算法与窗口更新规则取决于协议栈实现
TIME-WAIT主动关闭方等待 2MSL 确保最后 ACK 送达具体时长由实现决定;大量短连接会增加端口压力

基础问答

Q: 为什么 TCP 需要三次握手建立连接?

答: 防止旧的、已失效的连接请求报文突然到达服务器导致错误建立连接。如果只有两次握手,一个在网络中延迟的旧 SYN 到达服务器时,服务器会回复 SYN+ACK 并直接进入 ESTABLISHED 状态分配资源。三次握手中,客户端可以确认自己确实想建立这个连接(发送最后的 ACK),从而拒绝过期的连接请求。

Q: 四次挥手过程中为什么有 TIME-WAIT 状态?

答: TIME-WAIT 持续 2MSL(Maximum Segment Lifetime),具体秒数由协议栈实现决定。它有两个作用:一是确保主动关闭方发送的最后一个 ACK 丢失后,仍能收到对方重传的 FIN 并再次确认;二是让本连接期间在网络中滞留的报文有足够时间被丢弃,不会干扰使用相同四元组的后续连接。

Q: TCP 如何保证数据不丢失、不重复、不乱序?

答: 发送方按字节流位置分配序列号。接收方按序列号重组数据,检测到缺口时会重复发送当前累计确认号,也就是“下一次期望收到的字节序号”;发送方收到足够的重复 ACK 后可以快速重传。重复的数据段会按序列号识别并丢弃。

深入追问

Q: 经典 TCP Reno 中的慢启动、拥塞避免、快速重传和快速恢复分别解决什么问题?

答:

  • 慢启动:解决“尚不知道网络容量”的问题。从较小的初始拥塞窗口开始,按收到的确认快速增长;初始窗口大小取决于所遵循的 RFC 和协议栈实现。
  • 拥塞避免:解决”接近网络容量后如何稳定”的问题。线性增长 cwnd,避免剧烈的速率振荡。
  • 快速重传:解决”必须等到超时才发现丢包太慢”的问题。收到 3 个重复 ACK 即立即重传。
  • 快速恢复:解决“检测到部分丢包后不必像超时那样完全回到初始阶段”的问题。具体窗口调整规则依赖 Reno/NewReno 等算法及实现。

Q: 滑动窗口和拥塞窗口有什么区别?

答: 发送方的实际发送窗口 = min(接收方通告窗口, 拥塞窗口)。接收方通告窗口反映接收方的处理能力——“我能吃多少”;拥塞窗口反映发送方对网络容量的估计——“网络能传多少”。两者取最小值。

工程场景题

场景:嵌入式设备通过 TCP 发送数据,抓包发现多个小包被延迟或合并发送

分析思路:

  1. 检查 Nagle 算法是否启用:存在未确认数据时,它会暂存后续小块数据,直到收到 ACK 或积累到足够大小。
  2. 如果应用协议是低延迟的小请求-响应模式,可以评估启用 TCP_NODELAY;如果是批量传输模式,通常保留 Nagle 更合适。
  3. 也可能与对端延迟确认(Delayed ACK)产生不良交互。

场景:服务器上 netstat -an 显示大量 CLOSE-WAIT 连接不释放

分析思路:

CLOSE-WAIT 表示对方已发送 FIN,但本地应用还没有调用 close()。这说明应用层代码存在 bug——在收到对端关闭信号后没有正确关闭 socket。

检查:应用是否正确处理了 read() 返回 0(对端关闭连接)的情况?收到 0 后是否调用了 close()

易错点

  • 误区:“TCP 是可靠协议,所以数据 100% 能到” → TCP 在连接可维持时提供可靠、有序字节流;持续超时或网络中断后,协议栈会放弃重传并向应用报告连接错误,不保证一定发送 RST。
  • 误区:“TCP 有消息边界” → TCP 是字节流协议,没有消息边界。应用层必须自己设计帧协议。
  • 误区:“SYN 包中的序列号从 0 开始” → 初始序列号 ISN 应该是随机生成的(防序列号预测攻击)。
  • 误区:“TCP 关闭永远严格需要四个独立报文” → 常见流程是四次交互,因为被动关闭方可能还有数据要发送;如果它收到 FIN 时也已准备关闭,ACK 和 FIN 可以合并在同一个报文段中。

一句话总结

TCP 的可靠性建立在”每个字节编号、未确认则重传”的基础上,而三次握手防旧连接、滑动窗口做流控、拥塞控制适配网络——这四个机制是任何 TCP 面试的必考点。

官方参考资料