ServerSocket关闭后端口未释放问题求助
我来帮你排查下这个旧端口始终处于LISTENING状态的问题,结合你的代码来看,主要有几个关键原因和对应的解决方向:
1. ServerSocket未被显式关闭(核心问题)
你的监听线程代码中,ServerSocket serverSocket是在循环外部初始化的,但当你通过webClientConnector.setRunning(false)和webClientConnectorThread.interrupt()终止线程时,没有执行serverSocket.close()。
当serverSocket.accept()被中断时会抛出SocketException,但你的外层try-catch仅捕获异常并记录日志,并没有处理ServerSocket的关闭操作。这会导致ServerSocket资源一直被持有,端口也就无法释放。
解决方式:
使用try-with-resources语法自动管理ServerSocket和Socket资源,确保无论正常退出还是异常退出,资源都会被正确关闭:
try { int port = Integer.parseInt(globalParamService.findByName("serviceInterconnectPort").getValue()); // 用try-with-resources包裹ServerSocket,自动关闭 try (ServerSocket serverSocket = new ServerSocket(port)) { logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully started web client connector on port " + port); while(running) { // 每个客户端连接的Socket也用try-with-resources管理 try (Socket socket = serverSocket.accept()) { logEntryService.logInfo(LogEntry.CONNECTIVITY, "Incoming request from web client"); // 输入输出流同样用try-with-resources自动关闭 try (InputStream is = socket.getInputStream(); InputStreamReader isr = new InputStreamReader(is); BufferedReader br = new BufferedReader(isr)) { String message = br.readLine(); if (message.contains("Restart Web Client Connector")) { if (!main.isWebClientConnectorRestarting()) { main.restartWebClientConnector(); String returnMessage = "Done\n"; try (OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw)) { bw.write(returnMessage); bw.flush(); } } else { String returnMessage = "Request cancelled\n"; try (OutputStream os = socket.getOutputStream(); OutputStreamWriter osw = new OutputStreamWriter(os); BufferedWriter bw = new BufferedWriter(osw)) { bw.write(returnMessage); bw.flush(); } logEntryService.logWarning(LogEntry.CONNECTIVITY, "Web client connector restart request cancelled, restart already in progress"); } } } } catch (Exception ex) { logEntryService.logError(LogEntry.CONNECTIVITY, "Error handling client connection : " + ex.getMessage()); } } } // 此处ServerSocket会自动关闭,释放端口 } catch (Exception ex) { logEntryService.logError(LogEntry.CONNECTIVITY, "Error processing restart request from web client : " + ex.getMessage()); }
2. running变量未使用volatile修饰
如果你的running是普通boolean变量,JVM可能会对其进行缓存优化,导致监听线程无法及时感知到主线程设置的false值,进而继续阻塞在accept()方法上,ServerSocket也无法被关闭。
解决方式:
将running变量声明为volatile,确保主线程对它的修改能立即被监听线程看到:
private volatile boolean running = true;
3. 未等待旧线程完全终止就启动新线程
在restartWebClientConnector()方法中,你中断旧线程后立即调用initWebClientConnector()启动新线程,此时旧线程可能还在处理异常或关闭资源的过程中,旧的ServerSocket还未完全释放。
解决方式:
中断旧线程后,调用join()方法等待其完全终止,再初始化新的连接器:
public void restartWebClientConnector() { if (!webClientConnectorRestarting) { webClientConnectorRestarting = true; webClientConnector.setRunning(false); webClientConnectorThread.interrupt(); try { // 等待旧线程终止,设置5秒超时避免无限等待 webClientConnectorThread.join(5000); } catch (InterruptedException e) { logEntryService.logWarning(LogEntry.CONNECTIVITY, "Interrupted while waiting for old connector thread to terminate"); Thread.currentThread().interrupt(); // 保留中断状态 } initWebClientConnector(); logEntryService.logInfo(LogEntry.CONNECTIVITY, "Successfully restarted web client connector"); webClientConnectorRestarting = false; } }
按这几个方向调整后,旧端口应该能被正常释放了。
内容的提问来源于stack exchange,提问作者Martin

