TCP 三次握手和四次挥手,画个图就懂了#
三次握手流程#
TCP 三次握手大概是计网面试被问得最多的点了。背归背,但搞懂"为什么"比记住流程更重要。
先上个图:

大白话描述一下:
第一次握手:客户端发 SYN 包,带上初始序列号(ISN),“我想跟你建立连接”。客户端进入 SYN_SENT 状态。
第二次握手:服务端回 SYN+ACK 包,带上自己的初始序列号,同时确认收到了客户端的 SYN。“收到了,我也想连你”。服务端进入 SYN_RCVD 状态。
第三次握手:客户端再发 ACK 包确认。双方进入 ESTABLISHED 状态,连接建立。
客户端 服务端
| |
|------- SYN(seq=x) ---->| 第一次握手
| |
|<-- SYN+ACK(seq=y,ack=x+1) --| 第二次握手
| |
|------- ACK(ack=y+1) -->| 第三次握手
| |
| 连接建立完成 |为什么是三次,不是两次#
面试最爱追问的地方。
角度一:确认双方收发能力。三次握手后,双方都确认了"我能发、我能收、对方能发、对方能收"。只有两次的话,服务端没法确认客户端能不能收到它的消息。
角度二(更根本):防止历史连接。假设客户端发了一个 SYN,因网络延迟一直没到。客户端超时重发了新 SYN 并完成连接通信。后来那个迟到的旧 SYN 到了服务端。两次握手的话,服务端收到就建连了,但客户端压根不知道,白白浪费资源。有第三次握手,服务端得等 ACK,客户端不会理这个旧连接的 SYN-ACK,连接就不会误建。
我当初看书时第一个角度好理解,第二个角度想了一会儿才明白。
四次挥手流程#
断连比建连多一次,因为 TCP 是全双工的,每个方向要单独关闭。
客户端 服务端
| |
|------- FIN(seq=u) ---->| 第一次挥手:"我发完了"
| |
|<------ ACK(ack=u+1) ---| 第二次挥手:"知道了"
| |
| (服务端可能还有数据要发) |
| |
|<------ FIN(seq=w) -----| 第三次挥手:"我也发完了"
| |
|------- ACK(ack=w+1) -->| 第四次挥手:"好的"
| |
| TIME_WAIT (2MSL) |
| |为什么不能三次搞定?因为服务端收到客户端 FIN 时,可能还有数据没发完。它先 ACK 表示"知道你要关了",等数据发完再发自己的 FIN。
当然如果服务端恰好没数据要发了,第二三次可以合并。实际抓包有时候确实能看到三次挥手。
TIME_WAIT 到底干嘛的#
主动关闭的一方在四次挥手后不是直接关闭,而是进入 TIME_WAIT 状态,等 2 个 MSL(报文最大存活时间,Linux 上通常 60 秒)才真正关闭。
原因有两个:
保证最后的 ACK 到达:如果 ACK 丢了,被动关闭方会重发 FIN。TIME_WAIT 状态的一方还能收到并重新发 ACK。不然被动方就卡在 LAST_ACK 了。
让旧报文消亡:等 2MSL 确保网络中残留的老报文都超时丢弃了,不会干扰后续用同样端口建的新连接。
我之前在服务器上跑 netstat 看到过几万个 TIME_WAIT,当时以为是连接泄露,查了半天才知道是正常现象。高并发短连接场景这个问题会比较突出,可以调内核参数 tcp_tw_reuse 来复用 TIME_WAIT 的端口。
几个常见面试追问#
SYN 洪泛攻击是什么?
攻击者发大量 SYN 但不回 ACK,服务端维护大量半连接(SYN_RCVD 状态),耗尽资源。防御手段:SYN Cookie,让服务端无需保存半连接状态就能验证第三次握手的合法性。
第三次握手能带数据吗?
可以。第三次握手时客户端已经确认连接建立了,带数据是安全的。前两次不行,因为连接还没建好。
序列号为什么随机?
防攻击。如果序列号可预测,攻击者能伪造 TCP 包。随机 ISN 大大增加了伪造难度。
说到这里,TCP 连接管理看着简单,其实每个设计都有原因。建议用 Wireshark 自己抓个包看看,理论和实际结合起来印象深很多。我当时上计网课的时候抓了一次三次握手的包,对着看每个字段,比看书有用多了。