3. TCP 协议详解:报文段结构、三次握手与四次挥手

TCP(Transmission Control Protocol,传输控制协议)是面向连接的、可靠的、基于字节流的传输层协议。本章深入剖析 TCP 报文段的核心字段、连接建立与释放的完整过程,以及状态转换机制,为后续 Socket 编程打下坚实的理论基础。

3.1 TCP 报文段结构

TCP 将应用层数据封装为报文段(Segment),每个报文段包含头部和数据两部分。头部固定为 20 字节,可选项最多 40 字节。理解头部字段是掌握 TCP 行为的关键。

核心字段速览: 源端口(16 bit)、目的端口(16 bit)、序号(32 bit)、确认号(32 bit)、数据偏移(4 bit)、标志位(9 bit,常用 URG/ACK/PSH/RST/SYN/FIN)、窗口大小(16 bit)、校验和(16 bit)、紧急指针(16 bit)。
教学提示: 序号和确认号是 TCP 可靠传输的基石。序号表示本报文段数据部分的第一个字节的编号,确认号表示期望收到对方下一个报文段的第一个数据字节的序号。
TCP 报文段头部结构(固定 20 字节) 源端口(16 bit) 目的端口(16 bit) 序号(Sequence Number,32 bit) 确认号(Acknowledgment Number,32 bit) 数据偏移 保留 标志位(URG/ACK/PSH/RST/SYN/FIN) 窗口大小(16 bit) 校验和(16 bit) 紧急指针(16 bit) 选项(可选,最多 40 字节) + 填充(使头部为 4 字节整数倍)

3.1.1 标志位详解

标志位 含义 典型用途
URG 紧急指针有效 带外数据,如 Telnet 的 Ctrl+C
ACK 确认号有效 除 SYN 报文外,几乎所有报文都置 1
PSH 接收方应尽快交付应用层 交互式应用,如 SSH
RST 重置连接 异常终止、拒绝连接
SYN 同步序号,发起连接 三次握手的前两个报文
FIN 发送方完成数据发送 四次挥手的前两个报文

3.2 三次握手建立连接

TCP 在传输数据前必须建立连接,通过三次握手(Three-Way Handshake)同步双方的初始序号(ISN),确保双方具备收发能力。

核心目的: 1. 确认双方存在且可达;2. 协商初始序号,防止历史连接干扰;3. 分配缓冲区资源。

3.2.1 握手流程

  1. 第一次握手(客户端 → 服务器): 客户端发送 SYN 报文(SYN=1, seq=x),进入 SYN_SENT 状态。
  2. 第二次握手(服务器 → 客户端): 服务器回复 SYN+ACK 报文(SYN=1, ACK=1, seq=y, ack=x+1),进入 SYN_RCVD 状态。
  3. 第三次握手(客户端 → 服务器): 客户端发送 ACK 报文(ACK=1, seq=x+1, ack=y+1),双方进入 ESTABLISHED 状态。
TCP 三次握手流程 客户端 服务器 CLOSED → SYN_SENT LISTEN → SYN_RCVD ① SYN (seq=x) ② SYN+ACK (seq=y, ack=x+1) ③ ACK (seq=x+1, ack=y+1) ESTABLISHED ESTABLISHED
常见误区: 第三次握手可以携带数据。如果客户端在第三次握手时发送数据,服务器可以直接将数据交给应用层,减少一次 RTT(往返时间)。但前两次握手不能携带数据。

3.3 四次挥手断开连接

TCP 连接是全双工的,因此断开连接需要四次挥手(Four-Way Wavehand),确保双方都完成数据发送。

3.3.1 挥手流程

  1. 第一次挥手(主动关闭方 → 被动关闭方): 发送 FIN 报文(FIN=1, seq=u),进入 FIN_WAIT_1 状态。
  2. 第二次挥手(被动关闭方 → 主动关闭方): 回复 ACK 报文(ACK=1, seq=v, ack=u+1),进入 CLOSE_WAIT 状态。主动方收到后进入 FIN_WAIT_2。
  3. 第三次挥手(被动关闭方 → 主动关闭方): 被动方发送 FIN 报文(FIN=1, ACK=1, seq=w, ack=u+1),进入 LAST_ACK 状态。
  4. 第四次挥手(主动关闭方 → 被动关闭方): 发送 ACK 报文(ACK=1, seq=u+1, ack=w+1),进入 TIME_WAIT 状态,等待 2MSL 后关闭。
TCP 四次挥手流程 主动关闭方 被动关闭方 ① FIN (seq=u) FIN_WAIT_1 ② ACK (seq=v, ack=u+1) FIN_WAIT_2 CLOSE_WAIT ③ FIN (seq=w, ack=u+1) LAST_ACK ④ ACK (seq=u+1, ack=w+1) TIME_WAIT (2MSL) CLOSED
为什么需要 TIME_WAIT 状态? 1. 确保最后一个 ACK 能被被动关闭方收到(若丢失,被动方会重传 FIN);2. 使旧连接的所有报文在网络中消失,防止影响新连接。TIME_WAIT 持续 2 倍 MSL(最大报文段生存时间,通常为 1~2 分钟)。

3.4 TCP 状态转换图

TCP 连接的生命周期由 11 个状态组成,状态转换由报文收发和应用程序操作驱动。理解状态转换是排查网络故障和编写健壮 Socket 程序的基础。

TCP 状态转换图(简化版) CLOSED LISTEN SYN_SENT SYN_RCVD ESTABLISHED FIN_WAIT_1 CLOSE_WAIT FIN_WAIT_2 LAST_ACK TIME_WAIT 被动打开 主动打开 收到 SYN 收到 SYN+ACK 收到 ACK 主动关闭 收到 FIN 收到 ACK 收到 FIN+ACK 收到 FIN 关闭 收到 ACK 超时 2MSL
关键状态说明:
  • LISTEN: 服务器等待客户端连接,对应 Socket 编程中的 listen() 调用。
  • SYN_SENT: 客户端已发送 SYN,等待服务器回复,对应 connect() 调用。
  • SYN_RCVD: 服务器已收到 SYN 并回复 SYN+ACK,等待客户端 ACK。
  • ESTABLISHED: 连接已建立,可以正常收发数据。
  • TIME_WAIT: 主动关闭方等待 2MSL,确保被动方收到最后一个 ACK。

3.5 实战要点与常见问题

场景 现象 排查方向
连接超时 connect() 长时间无响应 检查防火墙、服务器是否在 LISTEN 状态、网络连通性
连接被拒绝 收到 RST 报文 端口未监听、服务未启动、防火墙拦截
大量 TIME_WAIT 服务器端口被占满 优化连接复用、调整 MSL 参数、使用长连接
半连接队列溢出 SYN 洪水攻击或高并发 调整 tcp_max_syn_backlog、启用 SYN Cookie
避坑指南: 在 Socket 编程中,如果服务器端 close() 后立即退出,客户端可能收到 RST 而非 FIN,导致连接异常终止。正确的做法是调用 shutdown() 先关闭写方向,等待对方 FIN 后再关闭读方向。

3.6 本章小结

  • TCP 报文段头部包含端口、序号、确认号、标志位、窗口等关键字段,是可靠传输的基础。
  • 三次握手同步序号、建立连接;四次挥手确保双方数据完整传输后断开连接。
  • 状态转换图是理解 TCP 生命周期和排查网络问题的核心工具。
  • TIME_WAIT 状态是 TCP 可靠性的重要保障,但高并发场景下需注意资源消耗。