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 可靠传输的基石。序号表示本报文段数据部分的第一个字节的编号,确认号表示期望收到对方下一个报文段的第一个数据字节的序号。
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 握手流程
- 第一次握手(客户端 → 服务器): 客户端发送 SYN 报文(SYN=1, seq=x),进入
SYN_SENT状态。 - 第二次握手(服务器 → 客户端): 服务器回复 SYN+ACK 报文(SYN=1, ACK=1, seq=y, ack=x+1),进入
SYN_RCVD状态。 - 第三次握手(客户端 → 服务器): 客户端发送 ACK 报文(ACK=1, seq=x+1, ack=y+1),双方进入
ESTABLISHED状态。
常见误区: 第三次握手可以携带数据。如果客户端在第三次握手时发送数据,服务器可以直接将数据交给应用层,减少一次 RTT(往返时间)。但前两次握手不能携带数据。
3.3 四次挥手断开连接
TCP 连接是全双工的,因此断开连接需要四次挥手(Four-Way Wavehand),确保双方都完成数据发送。
3.3.1 挥手流程
- 第一次挥手(主动关闭方 → 被动关闭方): 发送 FIN 报文(FIN=1, seq=u),进入
FIN_WAIT_1状态。 - 第二次挥手(被动关闭方 → 主动关闭方): 回复 ACK 报文(ACK=1, seq=v, ack=u+1),进入
CLOSE_WAIT状态。主动方收到后进入FIN_WAIT_2。 - 第三次挥手(被动关闭方 → 主动关闭方): 被动方发送 FIN 报文(FIN=1, ACK=1, seq=w, ack=u+1),进入
LAST_ACK状态。 - 第四次挥手(主动关闭方 → 被动关闭方): 发送 ACK 报文(ACK=1, seq=u+1, ack=w+1),进入
TIME_WAIT状态,等待 2MSL 后关闭。
为什么需要 TIME_WAIT 状态? 1. 确保最后一个 ACK 能被被动关闭方收到(若丢失,被动方会重传 FIN);2. 使旧连接的所有报文在网络中消失,防止影响新连接。TIME_WAIT 持续 2 倍 MSL(最大报文段生存时间,通常为 1~2 分钟)。
3.4 TCP 状态转换图
TCP 连接的生命周期由 11 个状态组成,状态转换由报文收发和应用程序操作驱动。理解状态转换是排查网络故障和编写健壮 Socket 程序的基础。
关键状态说明:
- 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 可靠性的重要保障,但高并发场景下需注意资源消耗。