Java面试之TCP三次握手与四次挥手

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里最典型的表现:

  • 频繁创建短连接的客户端(如每秒new一个Socket),很快耗尽本地端口,抛BindException: Address already in use
  • 服务端用固定端口(如8080)重启时,如果上次有连接处于TIME_WAIT,新进程可能bind失败
  • 解决方向不是Java层改代码,而是系统级调整:net.ipv4.tcp_tw_reuse = 1(允许复用TIME_WAIT socket,需时间戳支持)或缩短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堆满了。