高频面试点:深入理解 TCP 三次握手与四次挥手
在网络通信的世界里,TCP(Transmission Control Protocol,传输控制协议)是确保数据可靠传输的基石。其中,三次握手建立连接、四次挥手断开连接的过程,更是 Java 秋招面试中的高频考点。今天,我们就深入剖析这两个关键过程,结合原理、代码示例与面试真题,帮你吃透知识点 。
一、TCP 三次握手:建立可靠连接
(一)核心原理
TCP 连接的建立基于 三次握手 机制,目的是让客户端和服务器双方确认彼此收发数据的能力,同步初始序列号(ISN),为可靠传输打基础。过程如下:
- 第一次握手(客户端 → 服务器):客户端主动打开连接,发送
SYN
报文(SYN=1
,seq=x
,x
是客户端初始序列号 ),进入SYN-SENT
状态 。 - 第二次握手(服务器 → 客户端):服务器监听端口收到
SYN
报文,回复SYN+ACK
报文(SYN=1
,ACK=1
,seq=y
,ack=x+1
,y
是服务器初始序列号 ),进入SYN-RCVD
状态 。 - 第三次握手(客户端 → 服务器):客户端收到
SYN+ACK
报文,回复ACK
报文(ACK=1
,seq=x+1
,ack=y+1
),进入ESTABLISHED
状态;服务器收到ACK
报文后,也进入ESTABLISHED
状态,连接建立完成,双方开始数据传输
服务器客户端服务器客户端SYN=1, seq=x(进入 SYN-SENT)SYN=1, ACK=1, seq=y, ack=x+1(进入 SYN-RCVD)ACK=1, seq=x+1, ack=y+1(进入 ESTABLISHED)进入 ESTABLISHED(连接建立,可传输数据)TCP 三次握手流程
服务器客户端服务器客户端SYN=1, seq=x(进入 SYN-SENT)SYN=1, ACK=1, seq=y, ack=x+1(进入 SYN-RCVD)ACK=1, seq=x+1, ack=y+1(进入 ESTABLISHED)进入 ESTABLISHED(连接建立,可传输数据)TCP 三次握手流程
(二)Java 视角的理解(基于 Socket 编程)
在 Java 中,客户端通过 Socket
主动发起连接,服务器通过 ServerSocket
监听端口,底层会自动完成三次握手。示例代码片段:
// 服务器端(简化版)
ServerSocket serverSocket = new ServerSocket(8080); // 监听 8080 端口,等待客户端连接
Socket clientSocket = serverSocket.accept(); // 阻塞,直到三次握手完成,返回 Socket 对象用于通信// 客户端
Socket socket = new Socket("127.0.0.1", 8080); // 主动连接服务器,触发三次握手
当执行 new Socket(...)
时,客户端会先发送 SYN
报文;服务器 accept()
被触发后,回复 SYN+ACK
;客户端收到后再回复 ACK
,三次握手完成,Socket
才真正可用。
二、TCP 四次挥手:优雅断开连接
(一)核心原理
TCP 连接的断开需要 四次挥手 ,因为 TCP 是全双工通信,双方需要独立关闭各自的读写通道,过程如下:
- 第一次挥手(主动关闭方 → 被动关闭方):假设客户端主动关闭,发送
FIN
报文(FIN=1
,seq=u
),进入FIN-WAIT-1
状态,表示 “我这边没数据要发了” 。 - 第二次挥手(被动关闭方 → 主动关闭方):服务器收到
FIN
,回复ACK
报文(ACK=1
,seq=v
,ack=u+1
),进入CLOSE-WAIT
状态,告诉客户端 “你的关闭请求已收到,我处理完剩余数据就关闭” ;客户端收到ACK
后,进入FIN-WAIT-2
状态,等待服务器真正关闭 。 - 第三次挥手(被动关闭方 → 主动关闭方):服务器处理完数据,发送
FIN
报文(FIN=1
,ACK=1
,seq=w
,ack=u+1
),进入LAST-ACK
状态,表示 “我也没数据发了,准备关闭” 。 - 第四次挥手(主动关闭方 → 被动关闭方):客户端收到
FIN
,回复ACK
报文(ACK=1
,seq=u+1
,ack=w+1
),进入TIME-WAIT
状态;服务器收到ACK
后,进入CLOSED
状态。客户端等待2MSL
(最大报文段生存时间)后,确保服务器能收到ACK
(防止服务器重发FIN
时丢失),也进入CLOSED
状态
服务器(被动关闭)客户端(主动关闭)服务器(被动关闭)客户端(主动关闭)FIN=1, seq=u(进入 FIN-WAIT-1)ACK=1, seq=v, ack=u+1(进入 CLOSE-WAIT)进入 FIN-WAIT-2(收到服务器 ACK 后)FIN=1, ACK=1, seq=w, ack=u+1(进入 LAST-ACK)ACK=1, seq=u+1, ack=w+1(进入 TIME-WAIT)进入 CLOSED(收到客户端 ACK 后)等待 2MSL 后进入 CLOSEDTCP 四次挥手流程
服务器(被动关闭)客户端(主动关闭)服务器(被动关闭)客户端(主动关闭)FIN=1, seq=u(进入 FIN-WAIT-1)ACK=1, seq=v, ack=u+1(进入 CLOSE-WAIT)进入 FIN-WAIT-2(收到服务器 ACK 后)FIN=1, ACK=1, seq=w, ack=u+1(进入 LAST-ACK)ACK=1, seq=u+1, ack=w+1(进入 TIME-WAIT)进入 CLOSED(收到客户端 ACK 后)等待 2MSL 后进入 CLOSEDTCP 四次挥手流程
(二)Java 代码中的体现
基于 Java Socket,当一方调用 socket.close()
时,会触发四次挥手:
// 客户端主动关闭(示例)
Socket socket = new Socket("127.0.0.1", 8080);
// 数据传输...
socket.close(); // 触发第一次挥手(发送 FIN)
服务器端在读取数据时,若检测到客户端关闭(收到 FIN
),会先回复 ACK
(第二次挥手),然后处理完自身数据后,调用 socket.close()
触发第三次挥手(发送 FIN
);客户端收到服务器 FIN
后回复 ACK
(第四次挥手),完成关闭。
三、高频面试题 & 解答
(一)三次握手相关
问题 1:为什么需要三次握手,两次不行吗?
答:
三次握手的核心是 确认双方收发能力 。两次握手时,若客户端第一次 SYN
报文因网络延迟,超时后客户端重发 SYN
并建立连接、传输数据、关闭连接;此时延迟的 SYN
才到达服务器,服务器会认为是新连接,回复 SYN+ACK
,但客户端已关闭,不会处理 ACK
,服务器则会一直等待 ACK
,造成资源浪费。三次握手通过客户端最后回复的 ACK
,让服务器确认 “客户端能收到我的消息”,避免这种无效连接。
问题 2:三次握手过程中,客户端或服务器出现报文丢失,会发生什么?
答:
- 若客户端
SYN
丢失:客户端会超时重发SYN
,直到收到服务器SYN+ACK
或达到重试次数。 - 若服务器
SYN+ACK
丢失:客户端收不到回复,超时重发SYN
;服务器若收到重复SYN
,会重新回复SYN+ACK
。 - 若客户端
ACK
丢失:服务器收不到ACK
,会超时重发SYN+ACK
;客户端收到后,再次回复ACK
,直到服务器确认或超时。
(二)四次挥手相关
问题 1:为什么四次挥手需要四次,不能三次?
答:
因为 TCP 全双工特性,双方都要独立关闭读写。主动关闭方发 FIN
只是说 “我不发数据了”,但还能收数据;被动关闭方收到 FIN
后,要先回复 ACK
确认,然后处理完自己的发送队列,再发 FIN
表示 “我也不发了” 。这两个步骤(回复 ACK
和 发送 FIN
)无法合并,所以需要四次挥手。
问题 2:客户端 TIME-WAIT
状态为什么要等待 2MSL
?
答:
- 确保最后发送的
ACK
能到达服务器:若服务器没收到ACK
,会重发FIN
,客户端在2MSL
内可收到并重发ACK
,保证服务器正常关闭。 - 防止 “旧连接” 干扰新连接:
2MSL
可让本连接的所有报文都从网络中消失,新连接不会因残留报文出现混淆。