TCP 三次握手:连接是怎么建立的
更新时间:2026-09-17。网络协议域·传输层。状态迁移的全局视角见 TCP 状态机,建连在内核的队列实现见 listen 内核原理。
TCP 建立连接要交换 3 个报文,俗称三次握手(three-way handshake)。核心目的有两个:双向确认收发能力正常,并交换各自的初始序列号(ISN)。
一、三个报文
客户端 服务端
|------ SYN, seq=x --------->| 客户端 SYN_SENT;服务端收到后 SYN_RCVD
|<----- SYN+ACK, seq=y,ack=x+1 ----|
|------ ACK, ack=y+1 ------->| 双方 ESTABLISHED- 客户端发 SYN,带自己的初始序列号
seq=x。 - 服务端回 SYN+ACK:自己的 ISN
seq=y,同时确认ack=x+1。 - 客户端再发 ACK,确认
ack=y+1。连接在两端都进入 ESTABLISHED。
消耗的序列号:SYN 和 FIN 各占一个序列号(所以确认号是 x+1/y+1),纯 ACK 不占序号。ISN 不是从 0 开始,而是随时间递增的随机值,既防预测序列号的攻击,也区分新旧连接。
二、为什么是三次,不是两次或四次
这是最高频的面试题,本质是"两次信息往返(一来一回)就够双向确认,多一次浪费、少一次有缺陷":
- 为什么不能两次:两次只能让客户端确认服务端,并让服务端知道自己发的被收到,但服务端无法确认客户端具备接收能力(客户端对服务端 ISN 的确认是第三次 ACK 才带回的)。更关键的是防历史失效连接:一个网络中滞留的旧 SYN 若两次就建连,服务端会为一个客户端早已不想要的连接分配资源;三次握手里客户端可用第三个报文(对异常 SYN+ACK 回 RST)否决它。
- 为什么不需要四次:服务端的"确认客户端 SYN"和"自己的 SYN"可以合并在一个 SYN+ACK 报文里,所以理论上的四次被优化成三次。
用"双方都要确认自己发的对方能收、对方发的自己能收"的视角,三次是达成双向确认的最小值。
三、半连接队列、全连接队列与 backlog
握手在服务端内核里经过两个队列(高并发建连性能的关键):
| 队列 | 存放 | 时机 |
|---|---|---|
| 半连接队列(SYN queue) | 收到 SYN、回了 SYN+ACK,等最后 ACK | SYN_RCVD |
| 全连接队列(accept queue) | 握手完成、等应用 accept() 取走 | ESTABLISHED |
ss -lnt # Send-Q/Recv-Q 列:LISTEN 下 Recv-Q 是当前全连接队列长度,Send-Q 是 backlog 上限
nstat | grep -i 'listen' # ListenOverflows/ListenDrops 增长说明队列溢出
netstat -s | grep -i 'listen'应用
accept()不够快、全连接队列(backlog,约min(backlog, somaxconn))被填满时,新完成握手的连接会被丢弃或延迟,表现为客户端偶发连不上/建连慢。SYN 队列溢出则多与 SYN 洪水有关。详见 listen 内核原理 与 accept 瓶颈。
四、SYN 洪水与 SYN Cookie
攻击方发大量伪造源 IP 的 SYN,服务端为每个分配半连接资源,半连接队列被撑爆、正常用户无法建连(SYN flood,一种 DoS)。
- SYN Cookie:服务端不在半连接队列存状态,而是把连接信息编码进 ISN(cookie)发回;合法客户端的第三次 ACK 会带回这个 cookie,服务端据此无状态地重建连接。代价是部分 TCP 选项协商受限。
- 相关内核参数:
net.ipv4.tcp_syncookies、tcp_max_syn_backlog、tcp_synack_retries。 - 生产中还常用
tcp_tw_reuse、调somaxconn、以及在 L4 负载均衡(ipvsadm/LVS)层做 SYN 代理。
五、握手与性能/安全的关联
- 握手本身是一次 RTT 的延迟成本,短连接每次都要重新握手,是长连接/连接池、TLS 会话复用、TCP Fast Open 想省掉的开销。
- HTTPS 在三次握手之上还要叠加 TLS 握手(见 TLS 握手),RTT 更多,这正是 HTTPS 性能优化的重点。
- ISN 可预测曾导致连接劫持,现代内核用随机化 ISN;中间盒/防火墙也依赖握手状态放通后续报文。
相关阅读
状态流转见 TCP 状态机,关闭见四次挥手,队列溢出的观测调参见 listen 内核原理,HTTPS 额外成本见 TLS 握手。
总结
三次握手用 SYN→SYN+ACK→ACK 双向交换 ISN 并确认双方收发能力:两次无法防历史连接、四次可合并,三次是双向确认的最小集;服务端经半连接队列(SYN_RCVD)再到全连接队列(等 accept),队列溢出会丢连接,SYN 洪水用 SYN Cookie 无状态缓解。