694 字
2 分钟
TCP 连接建立过程:三次握手深度解析
前言
TCP 三次握手是网络编程的基石,也是面试必考知识点。本文深入剖析三次握手的每个细节,从协议层面理解连接建立的完整过程。
三次握手概览
sequenceDiagram
participant C as Client
participant S as Server
Note over C: CLOSED
Note over S: LISTEN
Note over C: 第一次握手
C->>S: SYN=1, seq=x
Note over C: SYN_SENT
Note over S: 收到 SYN
Note over S: 第二次握手
S->>C: SYN=1, ACK=1, seq=y, ack=x+1
Note over S: SYN_RCVD
Note over C: 收到 SYN+ACK
Note over C: 第三次握手
C->>S: ACK=1, seq=x+1, ack=y+1
Note over C: ESTABLISHED
Note over S: 收到 ACK
Note over S: ESTABLISHED
Note over C,S: 连接建立完成
一、TCP 报文结构
1.1 TCP 首部格式
0 1 2 3 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Source Port | Destination Port |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Sequence Number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Acknowledgment Number |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Data Offset | Reserved |U|A|P|R|S|F| Window || | |R|C|S|S|Y|I| || | |G|K|H|T|N|N| |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Checksum | Urgent Pointer |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+| Options | Padding |+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+1.2 关键标志位
| 标志位 | 名称 | 含义 |
|---|---|---|
| SYN | Synchronize | 发起连接 |
| ACK | Acknowledge | 确认 |
| FIN | Finish | 关闭连接 |
| RST | Reset | 重置连接 |
| PSH | Push | 推送数据 |
| URG | Urgent | 紧急数据 |
1.3 序列号与确认号
// 序列号(Sequence Number)// - 标识数据字节流的第一个字节的序号// - SYN 和 FIN 各占用一个序列号
// 确认号(Acknowledgment Number)// - 期望收到的下一个字节的序号// - 表示序号之前的所有字节都已收到
// 示例客户端发送: (SYN, (seq = 100));服务端回复: (SYN + ACK, (seq = 200), (ack = 101)); // 确认收到 SYN客户端发送: (ACK, (seq = 101), (ack = 201)); // 确认收到 SYN二、三次握手详细流程
2.1 第一次握手:客户端发送 SYN
flowchart TB
A[应用程序调用 connect] --> B[创建 TCP 控制块]
B --> C[选择初始序列号 ISN]
C --> D[构造 SYN 报文]
D --> E[发送 SYN]
E --> F[状态: SYN_SENT]
F --> G{等待 SYN+ACK}
SYN 报文结构:
Source Port: 54321Destination Port: 80Sequence Number: 1000 (ISN)Acknowledgment Number: 0Flags: SYN=1Window: 65535Options: - MSS = 1460 - Window Scale = 7 - SACK Permitted - TimestampISN(初始序列号)选择:
// Linux 内核 ISN 生成算法// 基于时钟 + MD5 哈希,防止预测攻击u32 secure_tcp_sequence_number(__be32 saddr, __be32 daddr, __be16 sport, __be16 dport){ u32 hash[MD5_DIGEST_WORDS];
hash[0] = saddr; hash[1] = daddr; hash[2] = sport << 16 | dport; hash[3] = tcp_clock_ms();
md5_transform(hash, key);
return hash[0];}2.2 第二次握手:服务端回复 SYN+ACK
flowchart TB
A[收到 SYN 报文] --> B[检查目标端口是否监听]
B -->|是| C[创建连接请求]
B -->|否| D[回复 RST]
C --> E[放入 SYN 队列]
E --> F[选择 ISN]
F --> G[构造 SYN+ACK]
G --> H[发送 SYN+ACK]
H --> I[状态: SYN_RCVD]
服务端 SYN+ACK 报文:
Source Port: 80Destination Port: 54321Sequence Number: 2000 (服务端 ISN)Acknowledgment Number: 1001 (客户端 ISN + 1)Flags: SYN=1, ACK=1Window: 65535Options: - MSS = 1460 - Window Scale = 7 - SACK Permitted - Timestamp2.3 第三次握手:客户端发送 ACK
flowchart TB
A[收到 SYN+ACK] --> B[验证 ACK 序号]
B --> C[构造 ACK 报文]
C --> D[发送 ACK]
D --> E[状态: ESTABLISHED]
E --> F[可以发送数据]
ACK 报文:
Source Port: 54321Destination Port: 80Sequence Number: 1001Acknowledgment Number: 2001Flags: ACK=1Window: 655352.4 服务端收到 ACK 后
flowchart TB
A[收到 ACK] --> B[验证序号]
B --> C[从 SYN 队列移除]
C --> D[移入 Accept 队列]
D --> E[状态: ESTABLISHED]
E --> F[等待应用 accept]
F --> G[应用调用 accept]
G --> H[返回已建立连接]
三、状态机与队列
3.1 TCP 状态转换
stateDiagram-v2
[*] --> CLOSED
CLOSED --> SYN_SENT: 主动打开/SYN
CLOSED --> LISTEN: 被动打开
LISTEN --> SYN_RCVD: 收到 SYN/发送 SYN+ACK
LISTEN --> CLOSED: 关闭
SYN_SENT --> ESTABLISHED: 收到 SYN+ACK/发送 ACK
SYN_SENT --> CLOSED: 超时/RST
SYN_RCVD --> ESTABLISHED: 收到 ACK
SYN_RCVD --> LISTEN: 超时
ESTABLISHED --> FIN_WAIT_1: 主动关闭/FIN
ESTABLISHED --> CLOSE_WAIT: 收到 FIN
FIN_WAIT_1 --> FIN_WAIT_2: 收到 ACK
FIN_WAIT_1 --> CLOSING: 收到 FIN+ACK
FIN_WAIT_2 --> TIME_WAIT: 收到 FIN
CLOSE_WAIT --> LAST_ACK: 发送 FIN
LAST_ACK --> CLOSED: 收到 ACK
CLOSING --> TIME_WAIT: 收到 ACK
TIME_WAIT --> CLOSED: 2MSL 超时
3.2 半连接队列与全连接队列
flowchart LR
subgraph 服务端
A[SYN 队列<br/>半连接队列] --> B[Accept 队列<br/>全连接队列]
end
C[SYN] --> A
A --> D[SYN+ACK]
E[ACK] --> A
A -.->|第三次握手后| B
F[accept] --> B
队列参数:
# 查看队列大小sysctl net.ipv4.tcp_max_syn_backlog # SYN 队列大小sysctl net.core.somaxconn # Accept 队列大小
# 查看队列状态netstat -s | grep "listen queue"ss -lnt # Recv-Q 显示当前 Accept 队列长度队列溢出处理:
| 情况 | 处理方式 |
|---|---|
| SYN 队列满 | 丢弃 SYN,不回复(或根据 tcp_syncookies 回复) |
| Accept 队列满 | 丢弃 ACK,客户端重传 |
3.3 SYN Cookies
防御 SYN Flood 攻击:
// SYN Cookie 生成// 将连接信息编码到 ISN 中u32 syn_cookie = MD5(saddr + daddr + sport + dport + secret)[0:24] + (timestamp % 24) << 24;
// 验证 SYN Cookie// 通过 ACK 的确认号反推连接信息# 启用 SYN Cookiessysctl -w net.ipv4.tcp_syncookies=1四、超时与重传
4.1 SYN 超时处理
sequenceDiagram
participant C as Client
participant S as Server
C->>S: SYN (第1次)
Note over C: 等待 1s
C->>S: SYN (第2次重传)
Note over C: 等待 2s
C->>S: SYN (第3次重传)
Note over C: 等待 4s
C->>S: SYN (第4次重传)
Note over C: 等待 8s
C->>S: SYN (第5次重传)
Note over C: 等待 16s
Note over C: 总计 31s 后放弃
Note over C: 返回 ETIMEDOUT
重传参数:
# 查看 TCP 超时参数sysctl net.ipv4.tcp_syn_retries # 主动打开 SYN 重试次数(默认 5)sysctl net.ipv4.tcp_synack_retries # 被动打开 SYN+ACK 重试次数(默认 5)sysctl net.ipv4.tcp_retries2 # 数据重传次数(默认 15)4.2 指数退避算法
// 重传时间计算(简化)const RTO = 1; // 初始重传超时时间const MAX_RETRIES = 5;
for (let i = 0; i < MAX_RETRIES; i++) { const timeout = RTO * Math.pow(2, i); console.log(`第 ${i + 1} 次重传,等待 ${timeout}s`);}
// 输出:// 第 1 次重传,等待 1s// 第 2 次重传,等待 2s// 第 3 次重传,等待 4s// 第 4 次重传,等待 8s// 第 5 次重传,等待 16s五、选项协商
5.1 TCP 选项格式
选项格式:Kind | Length | Data
常用选项:+------+------+------+| Kind | Len | Data |+------+------+------+| 2 | 4 | MSS | 最大段大小+------+------+------+| 3 | 3 | WS | 窗口缩放+------+------+------+| 4 | 2 | - | SACK 允许+------+------+------+| 8 | 10 | TS | 时间戳+------+------+------+5.2 MSS(最大段大小)
MSS = MTU - IP头(20) - TCP头(20)
以太网 MTU = 1500MSS = 1500 - 20 - 20 = 1460flowchart LR
A[应用数据 1460 字节] --> B[TCP 头 20 字节]
B --> C[IP 头 20 字节]
C --> D[以太网帧 1500 字节]
5.3 Window Scale
// 窗口缩放因子// 允许窗口大小超过 65535 字节
// 首部 Window 字段 16 位,最大 65535// Window Scale 选项指定左移位数
// 示例:Scale = 7// 实际窗口 = Window << 7// 最大窗口 = 65535 << 7 = 8,388,480 字节 ≈ 8MB5.4 SACK(选择性确认)
sequenceDiagram
participant S as 发送方
participant R as 接收方
S->>R: 数据 1-1000
S->>R: 数据 1001-2000
S->>R: 数据 2001-3000 (丢失)
S->>R: 数据 3001-4000
S->>R: 数据 4001-5000
R->>S: ACK=1001
R->>S: ACK=2001
R->>S: ACK=2001 + SACK=3001-4000
R->>S: ACK=2001 + SACK=3001-5000
Note over S: 只重传 2001-3000
S->>R: 数据 2001-3000
六、连接异常处理
6.1 RST 报文
触发 RST 的情况:
| 场景 | 原因 |
|---|---|
| 端口未监听 | 目标端口没有进程监听 |
| 连接已关闭 | 向已关闭的连接发送数据 |
| 序列号错误 | 收到序列号不在窗口内 |
| 请求超时 | SYN 队列溢出(特定配置下) |
| 主动中止 | 应用调用 RST 关闭 |
sequenceDiagram
participant C as Client
participant S as Server
C->>S: SYN → 端口 9999
Note over S: 端口未监听
S->>C: RST
Note over C: 连接被拒绝 ECONNREFUSED
6.2 连接超时
# 客户端超时connect() → ETIMEDOUT(127s,取决于配置)
# 服务端超时recv() → ETIMEDOUT(数据传输超时)
# Keepalive 超时sysctl net.ipv4.tcp_keepalive_time # 空闲多久开始探测(默认 7200s)sysctl net.ipv4.tcp_keepalive_intvl # 探测间隔(默认 75s)sysctl net.ipv4.tcp_keepalive_probes # 探测次数(默认 9)七、三次握手的意义
7.1 为什么是三次?
flowchart TB
subgraph 假设只有两次握手
A1[Client → SYN] --> A2[Server → SYN+ACK]
A2 --> A3[连接建立]
A3 --> A4[问题: 旧的 SYN 延迟到达]
A4 --> A5[Server 误建立连接]
end
subgraph 三次握手
B1[Client → SYN] --> B2[Server → SYN+ACK]
B2 --> B3[Client → ACK]
B3 --> B4[确认双方收发能力正常]
end
三次握手解决的问题:
- 防止旧的重复连接:延迟的 SYN 不会导致错误连接
- 同步序列号:双方都需要确认对方的 ISN
- 确认双向通信:验证双方的收发能力
7.2 防止 SYN Flood 攻击
flowchart LR
subgraph 攻击
A1[伪造 SYN] --> A2[SYN 队列满]
A2 --> A3[正常连接被拒绝]
end
subgraph 防御措施
B1[SYN Cookies] --> B2[无状态验证]
B3[减少 SYN+ACK 重试] --> B4[快速回收]
B5[增大队列] --> B6[提高容量]
end
# 防御配置sysctl -w net.ipv4.tcp_syncookies=1sysctl -w net.ipv4.tcp_synack_retries=2sysctl -w net.ipv4.tcp_max_syn_backlog=8192八、性能优化
8.1 快速打开(TFO)
TCP Fast Open 跳过三次握手:
sequenceDiagram
participant C as Client
participant S as Server
Note over C,S: 首次连接
C->>S: SYN + Cookie Request
S->>C: SYN+ACK + Cookie
Note over C: 保存 Cookie
Note over C,S: 后续连接
C->>S: SYN + Cookie + Data
Note over S: 验证 Cookie
S->>C: SYN+ACK + Response
Note over C,S: 0-RTT 数据传输
# 启用 TFO(Linux 3.7+)sysctl -w net.ipv4.tcp_fastopen=38.2 同时打开
两端同时发起连接:
sequenceDiagram
participant A as 端点 A
participant B as 端点 B
A->>B: SYN, seq=x
B->>A: SYN, seq=y
Note over A: SYN_SENT
Note over B: SYN_SENT
A->>B: ACK, ack=y+1
B->>A: ACK, ack=x+1
Note over A: ESTABLISHED
Note over B: ESTABLISHED
九、调试与诊断
9.1 抓包分析
# 使用 tcpdump 抓取三次握手tcpdump -i eth0 'tcp[tcpflags] & (tcp-syn|tcp-ack) != 0' -n
# 输出示例IP 192.168.1.100.54321 > 192.168.1.1.80: Flags [S], seq 1000IP 192.168.1.1.80 > 192.168.1.100.54321: Flags [S.], seq 2000, ack 1001IP 192.168.1.100.54321 > 192.168.1.1.80: Flags [.], ack 20019.2 状态查看
# 查看连接状态netstat -nat | grep ESTABLISHEDss -nat state established
# 查看 SYN_RECV 状态(可能有攻击)netstat -nat | grep SYN_RECVss -nat state syn-recv
# 统计各状态连接数netstat -nat | awk '{print $6}' | sort | uniq -c9.3 内核参数调优
# 增大 SYN 队列net.ipv4.tcp_max_syn_backlog = 8192net.core.somaxconn = 8192
# 减少重试次数(防御 SYN Flood)net.ipv4.tcp_synack_retries = 2net.ipv4.tcp_syn_retries = 3
# 启用 SYN Cookiesnet.ipv4.tcp_syncookies = 1
# 快速回收 TIME_WAITnet.ipv4.tcp_tw_reuse = 1
# 缩短 TIME_WAIT 时间net.ipv4.tcp_fin_timeout = 30总结
三次握手完整流程图
flowchart TB
subgraph 客户端
C1[connect 调用] --> C2[发送 SYN]
C2 --> C3[SYN_SENT]
C3 --> C4[收到 SYN+ACK]
C4 --> C5[发送 ACK]
C5 --> C6[ESTABLISHED]
end
subgraph 服务端
S1[listen 调用] --> S2[LISTEN]
S2 --> S3[收到 SYN]
S3 --> S4[创建请求入 SYN 队列]
S4 --> S5[发送 SYN+ACK]
S5 --> S6[SYN_RCVD]
S6 --> S7[收到 ACK]
S7 --> S8[移入 Accept 队列]
S8 --> S9[ESTABLISHED]
S9 --> S10[accept 返回]
end
C2 -.->|网络| S3
S5 -.->|网络| C4
C5 -.->|网络| S7
关键要点
- 三次握手:SYN → SYN+ACK → ACK
- 状态转换:CLOSED → SYN_SENT → ESTABLISHED
- 队列管理:SYN 队列 + Accept 队列
- 超时重传:指数退避算法
- 选项协商:MSS、Window Scale、SACK
- 安全防御:SYN Cookies、队列调优
支持与分享
如果这篇文章对你有帮助,欢迎支持作者或分享给更多人
TCP 连接建立过程:三次握手深度解析
https://blog.souloss.com/posts/principles/tcp-connection-establishment/ 部分信息可能已经过时
相关文章 智能推荐
1
DNS 解析完整流程:从域名到 IP
原理 深入剖析 DNS 解析的完整过程,从浏览器缓存到权威 DNS 服务器,涵盖递归查询、迭代查询、缓存策略、DNSSEC 等核心机制。
2
TLS 握手详解:从明文到加密通道
原理 深入剖析 TLS 1.2 和 TLS 1.3 握手的完整过程,包括密码套件协商、密钥交换、证书验证、会话恢复等核心机制。
3
TCP连接管理:三次握手与四次挥手的工程细节
互联网运作 TCP如何建立和释放连接?三次握手为什么不能是两次?四次挥手为什么不能是三次?TIME_WAIT到底在等什么?从报文格式到状态机,从SYN Flood防御到连接异常处理,用抓包实验观察TCP连接的完整生命周期。
4
为什么 TCP 建立连接需要三次握手
技术科普 深入解析 TCP 建立连接时三次握手的设计原理,为什么两次握手无法保证可靠性,四次握手是否必要。
5
HTTP/1.1:持久连接
web 在 HTTP/1.0 的基础上,深入探索 HTTP/1.1 的核心改进——持久连接、分块传输编码、请求管道化、内容协商、缓存增强,以及 Host 头的重要性。通过 Python 实验服务器亲手体验这些特性带来的性能提升。






