Java中TCP三次握手由内核完成,new Socket()失败时抛ConnectException或SocketTimeoutException;四次挥手由close()触发FIN,主动方进入TIME_WAIT致端口复用失败,需系统参数优化。

Java面试里问TCP三次握手和四次挥手,不是考你背流程,而是看你会不会把网络底层和Java应用行为联系起来——比如Socket连接建立失败时抛什么异常、close()调用后连接状态怎么变、为什么TIME_WAIT会导致端口无法重用。
三次握手在Java里怎么体现?
Java本身不直接发SYN包,但所有基于Socket的连接(如new Socket(host, port))都依赖内核完成三次握手。这个过程对Java代码是透明的,但失败时会暴露细节:
-
ConnectException: Connection refused:服务端没监听,或防火墙拦截,对应第二次握手(SYN-ACK)没回来 -
SocketTimeoutException:客户端设置socket.connect(addr, timeout)后超时,通常是第一次SYN发出后没收到任何响应(网络不通或服务端丢包) - 握手成功后,
socket.isConnected()返回true,但socket.isClosed()仍为false——这两个状态互不干扰,别混淆
四次挥手谁先触发?Java代码怎么影响它?
谁先调用close()谁就是主动方,触发FIN。Java里常见两种场景:
- 客户端主动断开:
socket.close()→ 内核发FIN,进入FIN_WAIT_1状态 - 服务端主动断开(如Tomcat处理完HTTP请求后关闭连接):调用
Socket.close()或ServerSocket.close()都会触发对应连接的挥手 - 注意:
socket.shutdownOutput()只发FIN,不关闭Socket对象,还能读数据;而close()既发FIN又释放资源
TIME_WAIT为什么让Java程序“端口被占用”?
主动关闭方最后进入TIME_WAIT(持续2MSL,通常60秒),期间该四元组(源IP+端口+目标IP+端口)不能复用。Java里最典型的表现:
立即学习“Java免费学习笔记(深入)”;
- 频繁创建短连接的客户端(如每秒new一个
Socket),很快耗尽本地端口,抛BindException: Address already in use - 服务端用固定端口(如8080)重启时,如果上次有连接处于
TIME_WAIT,新进程可能bind失败 - 解决方向不是Java层改代码,而是系统级调整:
net.ipv4.tcp_tw_reuse = 1(允许复用TIME_WAITsocket,需时间戳支持)或缩短net.ipv4.tcp_fin_timeout
面试真题常挖的坑点
别只答“三次握手建立连接,四次挥手断开连接”。面试官盯着的是边界情况:
- 为什么不是三次挥手?因为TCP全双工,双方要各自确认“我发完了”和“我收到了你发完的信号”
-
close()调用后,Java线程立刻返回,但内核可能还在发FIN或等ACK——所以socket.isClosed()为true不代表网络上挥手已完成 - 如果服务端在
ESTABLISHED状态直接kill -9进程,客户端read()会立即返回-1(对端关闭),但write()可能等到下一次发包才报IOException: Broken pipe
真正卡住人的从来不是流程图,而是看到Connection reset异常时,能不能快速判断是对方RST了还是自己FIN后还写了数据;或者线上服务重启失败,能不能想到去ss -tan state time-wait | wc -l看看是不是TIME_WAIT堆满了。










