Java学习第八十七部分——四次挥手
目录
一、前言提要
二、详细过程——总体上分析
三、详细过程——结合Java代码分析
四、关键问题解析
五、总结归纳概述
一、前言提要
TCP 使用四次挥手(Four-way Handshake)来安全关闭连接,确保双方数据传输完整。
二、详细过程——总体上分析
(一)流程图
(二)步骤详解
第一次挥手(FIN)
主动方(如客户端):发送 FIN=1
(seq=u
),进入 FIN-WAIT-1
状态,表示不再发送数据。
第二次挥手(ACK)
被动方(如服务器):返回 ACK=1
(ack=u+1
),进入 CLOSE-WAIT
状态,但仍可发送剩余数据。
主动方: 收到后进入 FIN-WAIT-2
状态。
第三次挥手(FIN)
被动方 :发送 FIN=1
(seq=v
),进入 LAST-ACK
状态,表示不再发送数据。
第四次挥手(ACK)
主动方 :返回 ACK=1
(ack=v+1
),进入 TIME-WAIT
状态(等待 2MSL
时间)
被动方 :立即关闭。
步骤 | 发送方 | TCP标志 | Java对应操作 | 状态变化 |
---|---|---|---|---|
第一次挥手 | 客户端 | FIN | socket.close() | FIN-WAIT-1 |
第二次挥手 | 服务器 | ACK | in.read() 返回-1 | CLOSE-WAIT |
第三次挥手 | 服务器 | FIN | socket.close() | LAST-ACK |
第四次挥手 | 客户端 | ACK | 自动发送ACK | TIME-WAIT → CLOSED |
缩写 | 全称 | 含义 |
---|---|---|
FIN | Finish | 终止连接标志位(FIN=1 表示请求关闭连接)。 |
ACK | Acknowledgment | 确认标志位,ack 值为对端FIN 的序列号+1(确认关闭请求)。 |
(三)白话解释
1. 客户端:我不需要请求了,可以关闭连接了
2. 服务端:我知道你要关闭了
3. 服务端:我发送完成了,可以关闭了
4. 客户端:我知道你发送完成了,确认关闭
三、详细过程——结合Java代码分析
(一)流程图同上
(二)步骤详解
第一次挥手(FIN)
Java代码:socket.close()
触发FIN包。
第二次挥手(ACK)
Java代码:服务器 InputStream.read()
返回 -1
表示客户端已关闭。
第三次挥手(FIN)
Java代码:服务器显式调用 socket.close()
。
第四次挥手(ACK)
服务器收到 ACK
后立即关闭连接。
(三)Java Socket 和 TCP 状态的关系
Java 代码 | TCP 状态 | 说明 |
---|---|---|
new ServerSocket(8888) | LISTEN | 服务器监听端口,等待连接 |
serverSocket.accept() | SYN_RECEIVED | 收到SYN,发送SYN+ACK |
new Socket() | SYN_SENT | 客户端发送SYN |
连接建立后(accept() 返回) | ESTABLISHED | 三次握手完成,可以通信 |
socket.close() | FIN_WAIT / CLOSE | 关闭连接,进入四次挥手阶段 |
(四)核心要点
-
四次挥手确保双向连接安全关闭。
-
Java的 `socket.close()` 触发FIN包,但需注意显式关闭资源。
-
`TIME-WAIT` 是客户端的保护机制,`CLOSE-WAIT` 需防止积压。
(五)代码见下
客户端——主动关闭
Socket socket = new Socket("localhost", 8888);
System.out.println("客户端连接成功!");// 发送数据
PrintWriter out = new PrintWriter(socket.getOutputStream(), true);
out.println("Hello, Server!");// 主动关闭连接(第一次挥手)
socket.close(); // 发送FIN
System.out.println("客户端已关闭连接");
服务器——处理关闭
ServerSocket serverSocket = new ServerSocket(8888);
Socket socket = serverSocket.accept();
System.out.println("客户端已连接");// 接收数据
BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
String message = in.readLine(); // 读取数据
System.out.println("收到客户端消息:" + message);// 检测客户端是否关闭(第二次挥手:返回ACK)
if (message == null) { // read()返回-1表示客户端已关闭System.out.println("客户端已关闭,准备关闭服务器...");
}// 服务器主动关闭(第三次挥手)
socket.close(); // 发送FIN
serverSocket.close();
System.out.println("服务器已关闭");
四、关键问题解析
(1)为什么需要四次挥手?
-
全双工通信:因为TCP连接是双向的,关闭时需要分别关闭发送和接收通道。
-
客户端发送 `FIN` 表示不再发送数据,但仍可接收数据(半关闭)。
-
服务器需要先确认客户端的 `FIN`(第二次挥手),再发送自己的 `FIN`(第三次挥手)。
(2)TIME-WAIT状态的作用?
-
防止最后一个ACK丢失:如果客户端直接关闭,服务器可能因未收到 `ACK` 而重发 `FIN`,导致新连接收到旧数据。
-
等待时间:`2MSL`(Maximum Segment Lifetime,通常2分钟),确保网络中所有残留数据包消失。
(3)CLOSE-WAIT状态积压问题
-
如果服务器不调用 `socket.close()`,会一直处于 `CLOSE-WAIT` 状态,导致连接泄漏。
-
解决方案:确保服务器在检测到客户端关闭后,显式关闭Socket。
五、总结归纳概述
-
TCP四次挥手是双方双向关闭连接的过程:客户端先发FIN → 服务器回ACK → 服务器发FIN → 客户端回ACK,确保数据完整传输并安全释放资源。
-
本质:全双工通信需分别关闭两个方向,比三次握手多一次确认