Java Socket编程:TCP三次握手与四次挥手详解

一、TCP连接建立(三次握手)

在Java Socket中,当客户端执行new Socket(serverIP, port)时触发三次握手:

  1. SYN(客户端 → 服务端)
    客户端发送SYN包($syn=1, seq=x$),进入SYN_SENT状态
  2. SYN+ACK(服务端 → 客户端)
    服务端响应SYN-ACK包($syn=1, ack=1, seq=y, ack_num=x+1$),进入SYN_RCVD状态
  3. ACK(客户端 → 服务端)
    客户端确认($ack=1, ack_num=y+1$),双方进入ESTABLISHED状态,连接建立
// 客户端触发握手
try (Socket clientSocket = new Socket("192.168.1.100", 8080)) {
    // 连接建立后执行操作
}

二、TCP连接终止(四次挥手)

在Java中调用socket.close()时触发四次挥手:

  1. FIN(主动关闭方 → 被动方)
    发送FIN包($fin=1, seq=u$),进入FIN_WAIT_1状态
  2. ACK(被动方 → 主动方)
    被动方确认($ack=1, ack_num=u+1$),主动方进入FIN_WAIT_2状态
  3. FIN(被动方 → 主动方)
    被动方发送FIN包($fin=1, seq=v$),进入LAST_ACK状态
  4. ACK(主动方 → 被动方)
    主动方确认($ack=1, ack_num=v+1$),进入TIME_WAIT(等待2MSL后关闭)
// 服务端关闭连接示例
try (ServerSocket server = new ServerSocket(8080);
     Socket socket = server.accept()) {
    // 通信结束后关闭
    socket.close();  // 触发挥手过程
}

三、关键机制解析
  1. 序列号保障可靠性
    每次传输的序列号满足:$seq_{new} = seq_{old} + data_len$
    确认号规则:$ack_num = received_seq + 1$

  2. TIME_WAIT状态作用

    • 确保最后一个ACK到达(重传丢失的FIN)
    • 允许旧报文在网络中消亡(等待2MSL时间)
  3. 半关闭状态
    调用shutdownOutput()时发送FIN,但仍可接收数据,对应四次挥手中的步骤1-2

四、常见问题与解决方案
问题现象原因解决方法
BindException端口处于TIME_WAIT设置SO_REUSEADDR选项
连接超时SYN包未收到ACK检查防火墙/网络配置
ConnectionReset收到RST包检查对端是否异常关闭

重要提示:在Socket编程中需显式关闭资源,避免连接泄漏:

// 推荐写法(Java 7+ try-with-resources)
try (Socket socket = new Socket();
     OutputStream out = socket.getOutputStream()) {
    // 业务代码
}  // 自动调用close()触发四次挥手

五、Wireshark抓包分析

通过抓包工具可观察:

  • 握手阶段:SYN → SYN-ACK → ACK
  • 挥手阶段:FIN → ACK → FIN → ACK
  • 序列号变化符合:$ack_num = seq_{received} + 1$

理解TCP状态机对调试网络问题至关重要,建议结合netstat -ano命令监控Socket状态变化。

Logo

Agent 垂直技术社区,欢迎活跃、内容共建。

更多推荐