Java客户端关闭Socket时抛出SocketException的原因及处理方法
异常触发原因
你代码的执行流程中,当主线程的Scanner检测到输入终止(比如用户按下Ctrl+D/Ctrl+Z结束控制台输入),会退出输入循环进入finally块执行socket.close()操作。但此时你启动的后台读线程仍处于运行状态,正阻塞在in.readLine()调用等待服务端返回数据,socket被关闭后,这个 pending 的IO操作会直接抛出java.net.SocketException: Socket closed异常,这就是报错的来源。
该行为是否属于正常现象
这属于主动关闭资源时的预期正常现象,不属于业务逻辑故障,是JDK Socket实现的标准反馈,仅代表关闭socket时还有未完成的IO操作在运行,不代表客户端运行过程中出现了功能问题。
正确的处理方案
- 给后台读线程增加退出控制逻辑,替换无边界的
while(true)死循环,同时对Socket关闭触发的异常做特殊处理,不用打印冗余错误栈。 - 关闭socket前先更新线程运行标志,关闭socket后等待读线程安全退出,避免资源泄漏。
- 增加服务端主动断连的兼容判断,
readLine()返回null时代表服务端已经关闭连接,直接退出读循环即可。
修改后的参考代码如下:
public static void main(String[] args) throws UnknownHostException, IOException { Socket socket = null; AtomicBoolean isRunning = new AtomicBoolean(true); Thread readThread = null; try { socket = new Socket("localhost", 9101); PrintWriter out = new PrintWriter(socket.getOutputStream()); BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); readThread = new Thread(new Runnable() { @Override public void run() { while(isRunning.get()) { try { String line = in.readLine(); // 服务端主动关闭连接时readLine返回null,直接退出 if (line == null) { break; } System.out.println(line); } catch (SocketException e) { // 客户端主动关闭socket触发的异常,属于正常退出场景,无需打印错误 break; } catch (IOException e) { e.printStackTrace(); System.out.println("read error"); break; } } } }); readThread.start(); System.out.println("Write to server:"); Scanner scanner = new Scanner(System.in); while (scanner.hasNextLine()) { String message = scanner.nextLine(); out.println(message); out.flush(); }; } finally { // 先修改线程运行标志 isRunning.set(false); // 关闭socket if (socket != null && !socket.isClosed()) { socket.close(); } // 等待读线程安全退出 if (readThread != null) { try { readThread.join(); } catch (InterruptedException e) { Thread.currentThread().interrupt(); } } } }
内容的提问来源于stack exchange,提问作者Ineversurrender93
相关产品推荐
相关产品推荐

