TCP 三次握手
TCP 协议概述
TCP(Transmission Control Protocol)是一种面向连接的、可靠的传输层协议。在数据传输之前必须先通过三次握手建立连接,传输结束后通过四次挥手释放连接。
TCP 报文段关键字段
| 字段 | 位数 | 作用 |
|---|---|---|
| 序列号(Seq) | 32 | 标识本段数据的第一个字节的序号 |
| 确认号(Ack) | 32 | 期望收到对方下一个段的序列号 |
| SYN | 1 | 同步标志,用于建立连接 |
| ACK | 1 | 确认标志,确认号有效 |
| FIN | 1 | 结束标志,发送方已无数据 |
| RST | 1 | 复位标志,异常终止连接 |
三次握手(连接建立)
客户端 服务端
| |
| —— [SYN] Seq=x, Ack=0 ——→ | 第一次
| CLOSED → SYN-SENT | LISTEN → SYN-RCVD
| |
| ←—— [SYN+ACK] Seq=y, Ack=x+1 —— | 第二次
| SYN-SENT → ESTABLISHED |
| |
| —— [ACK] Seq=x+1, Ack=y+1 ——→ | 第三次
| | SYN-RCVD → ESTABLISHED
第一次(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。服务端收到后也进入 ESTABLISHED。第三次握手可以携带数据。
为什么不是两次? 两次握手只能验证客户端发送和服务端接收。第三次让服务端确认自己的发送能力和客户端的接收能力,同时防止过期 SYN 导致服务端误开连接。
四次挥手(连接释放)
TCP 全双工,每个方向必须独立关闭:
客户端(主动关闭) 服务端(被动关闭)
ESTABLISHED ESTABLISHED
| |
| —— [FIN] Seq=u, Ack=v ——→ | 第一次
| → FIN-WAIT-1 | → CLOSE-WAIT
| |
| ←—— [ACK] Seq=v, Ack=u+1 —— | 第二次
| → FIN-WAIT-2 |
| |
| ←—— [FIN+ACK] Seq=w, Ack=u+1 —— | 第三次
| | → LAST-ACK
| |
| —— [ACK] Seq=u+1, Ack=w+1 ——→ | 第四次
| → TIME-WAIT (等 2MSL ≈ 60s) | → CLOSED
| → CLOSED |
第一次(FIN):客户端发 FIN=1,表示”我没数据了”,进入 FIN-WAIT-1。
第二次(ACK):服务端确认收到 FIN,进入 CLOSE-WAIT。客户端进入 FIN-WAIT-2。
第三次(FIN+ACK):服务端发完剩余数据后,发 FIN=1,进入 LAST-ACK。
第四次(ACK):客户端确认后进入 TIME-WAIT,等待 2MSL 后关闭。
TIME-WAIT 为何等 2MSL? (1) 确保最后 ACK 能被服务端收到;(2) 让旧连接报文完全消失,避免干扰新连接。
状态变迁汇总
| 阶段 | 客户端状态 | 服务端状态 |
|---|---|---|
| 初始 | CLOSED | LISTEN |
| 握手完成 | ESTABLISHED | ESTABLISHED |
| 主动关闭 | FIN-WAIT-1 → FIN-WAIT-2 → TIME-WAIT → CLOSED | - |
| 被动关闭 | - | CLOSE-WAIT → LAST-ACK → CLOSED |
标志位速查
| 阶段 | SYN | ACK | FIN |
|---|---|---|---|
| 握手-1 | 1 | 0 | 0 |
| 握手-2 | 1 | 1 | 0 |
| 握手-3 | 0 | 1 | 0 |
| 挥手-1 | 0 | 0 | 1 |
| 挥手-2 | 0 | 1 | 0 |
| 挥手-3 | 0 | 1 | 1 |
| 挥手-4 | 0 | 1 | 0 |
SYN Flood 攻击与 SYN Cookie 防御
攻击原理
SYN Flood 是典型的 DoS 攻击:攻击者伪造大量不存在的 IP 地址,向服务端发送 SYN 包。服务端收到后为每个 SYN 分配资源(TCB,传输控制块)并回复 SYN+ACK,进入 SYN-RCVD 状态等待 ACK。由于源 IP 是伪造的,ACK 永远不会到达,半连接队列被占满,导致正常用户无法建立连接。
攻击者 服务端
| —— SYN (伪造IP-A) ——→ | 分配TCB → SYN-RCVD
| —— SYN (伪造IP-B) ——→ | 分配TCB → SYN-RCVD
| —— SYN (伪造IP-C) ——→ | 分配TCB → SYN-RCVD
| ... (大量) ... | 半连接队列满!
| |
正常用户 —— SYN ——→ | ❌ 丢弃,无法响应
SYN Cookie 防御
SYN Cookie 的核心思想:收到 SYN 时不分配资源,而是将连接信息编码到序列号中返还给客户端,只有收到合法 ACK 时才分配资源。
具体流程:
- 服务端收到 SYN,不分配 TCB
- 服务端计算
cookie = hash(源IP, 源端口, 目的IP, 目的端口, 时间戳, 密钥) - 将 cookie 作为初始序列号
Seq=y在 SYN+ACK 中返回 - 如果客户端是真实的,会回复
ACK, Ack=y+1;攻击者(伪造 IP)收不到 SYN+ACK,不会回复 - 服务端验证收到的
Ack-1是否等于hash(源IP, 源端口, 目的IP, 目的端口, 时间戳, 密钥) - 验证通过才分配 TCB 并建立连接
SYN Cookie 是 Linux 内核的默认防御策略。当半连接队列超过阈值时自动启用,无需手动配置。