极高 进阶
TCP三次握手与四次挥手#
一句话答案#
三次握手建立连接(SYN→SYN+ACK→ACK),四次挥手关闭连接(FIN→ACK→FIN→ACK),确保双方可靠通信。
核心要点
三次握手过程:
客户端 服务端(LISTEN)
│── SYN(seq=x)──────────────→│ 第1次:客户端进入 SYN_SENT
│← SYN-ACK(seq=y, ack=x+1)──│ 第2次:服务端进入 SYN_RCVD(放入半连接队列)
│── ACK(ack=y+1)────────────→│ 第3次:客户端先进入 ESTABLISHED,服务端收到后进入 ESTABLISHED(移入全连接队列)
│ │
├──────── 连接建立,可以传数据 ──┤plaintext四次挥手过程(以客户端主动关闭为例):
客户端(主动关闭) 服务端(被动关闭)
│── FIN(seq=u)──────────────→│ 第1次:客户端进入 FIN_WAIT_1
│←──────────── ACK(ack=u+1)──│ 第2次:服务端进入 CLOSE_WAIT;客户端收到后进入 FIN_WAIT_2(半关闭,服务端仍可发数据)
│←──────────── FIN(seq=w)────│ 第3次:服务端数据发完、应用调用 close() 后发 FIN,进入 LAST_ACK
│── ACK(ack=w+1)────────────→│ 第4次:客户端进入 TIME_WAIT,等 2MSL 后 CLOSED;服务端收到 ACK 后 CLOSEDplaintext- 双方同时发 FIN 时走「同时关闭」:FIN_WAIT_1 → CLOSING → TIME_WAIT,双方都会进入 TIME_WAIT
- 被动方收到 FIN 时如果没有数据要发,第 2、3 次可以合并成一个 FIN+ACK(配合延迟确认),抓包常看到”三次挥手”
为什么需要三次,而不是两次?
防止”历史失效连接请求”造成资源浪费:
场景(只有两次握手的问题):
T1: 客户端发出 SYN1(因网络延迟,迟迟未到达服务端)
T2: 客户端超时,重发 SYN2,正常建立连接,用完后关闭
T3: 延迟的 SYN1 终于到达服务端
→ 两次握手:服务端收到 SYN1,发 SYN-ACK,认为连接建立
→ 服务端分配资源,等待客户端数据
→ 客户端早已关闭,不会响应(连接对客户端来说是"失效的")
→ 服务端的资源白白占用(直到超时释放)plaintext三次握手的第三次 ACK 的作用:
- 让服务端确认客户端”此时仍然活着且愿意建立连接”
- 如果是历史 SYN:服务端回的 SYN-ACK 到达客户端后,客户端发现 ack 号对不上自己当前的连接(或根本没有这个连接),会回 RST,服务端收到 RST 就释放这个半连接,不会进入 ESTABLISHED
三次握手还确认了双方的初始序列号(ISN):
- 服务端知道客户端的初始序列号(x)
- 客户端知道服务端的初始序列号(y)
- 双方都能确认对方”收到了我的 SYN”,保证了双向通信能力
为什么不需要四次?
- 服务端可以在一个报文里同时发 SYN + ACK(合并为 SYN-ACK),节省一次握手
面试回答(2分钟版)
三次握手的过程是:客户端发SYN报文携带初始序列号x,服务端回复SYN-ACK携带自己的初始序列号y并确认ack=x+1,客户端再发ACK确认ack=y+1,连接建立。为什么必须三次而不是两次,核心原因是防止历史失效连接请求导致服务端资源浪费。假设只有两次握手,客户端之前发的一个延迟SYN到达服务端,服务端回SYN-ACK就认为连接建立了,分配资源等待数据,但客户端早已关闭不会响应,资源就白白占用。三次握手的第三次ACK让服务端确认客户端此刻仍然活着且愿意通信。同时三次握手也完成了双方初始序列号ISN的同步,保证后续可靠传输。四次挥手多一次的原因是TCP是全双工的,收到对方FIN后只是表示对方不再发数据,自己可能还有数据要发完,所以ACK和FIN分成两次发送,中间存在半关闭状态。关闭发起方最后进入TIME_WAIT状态等待2个MSL,确保最后的ACK能到达对方。
追问与易错
追问方向:
- “为什么握手三次不是两次?”→ 防止历史失效的 SYN 到达服务端导致资源浪费,第三次 ACK 让服务端确认客户端此刻仍活跃且愿意通信
- “为什么挥手需要四次?”→ TCP 全双工,收到 FIN 只表示对方不发了,自己可能还有数据要发完,ACK 和 FIN 之间存在半关闭状态所以不能合并
- “SYN Flood 攻击原理和防护?”→ 伪造源 IP 发大量 SYN 填满半连接队列;防护用 tcp_syncookies(Linux 默认 1:半连接队列溢出时才启用,把连接信息编码进 SYN-ACK 的序列号,不占队列资源,等第三次 ACK 回来再校验还原)+ 调大 SYN 队列 + 防火墙过滤
易错点:
- ❌ “三次握手是为了可靠”——RFC 9293 原文说主要原因是防止历史重复的连接请求造成混乱,同时完成双方初始序列号的同步
- ❌ “第三次握手不能携带数据”——可以的,此时客户端已确认连接建立(第一次 SYN 在标准握手里不带应用数据,开启 TCP Fast Open(RFC 7413)后重连时 SYN 可以带数据)