You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

Java Socket服务器断开异常测试未触发IOException问题排查

问题原因及解决方案

核心问题分析

1. 服务器关闭逻辑未清理已建立连接

你的SocketServer的closeServerSocket()方法仅关闭了用于监听新连接的ServerSocket,但没有关闭已经通过accept()建立的client Socket。TCP连接是双向的,只要一端的Socket未关闭,连接就会保持活跃状态,客户端无法感知服务器已停止服务,自然不会抛出IOException。

2. 服务器构造函数存在变量覆盖bug

构造函数中你定义了局部变量server,覆盖了类成员变量:

// 错误写法:局部变量覆盖成员变量
ServerSocket server = new ServerSocket(tcpPort,0,ipAdress);

这会导致成员变量server始终为null,调用closeServerSocket()时会触发NullPointerException,实际根本没关闭ServerSocket——这可能是测试中服务器看似还在响应的关键原因之一。

3. 客户端读取逻辑不可靠

客户端doStuff()依赖in.available()判断是否有数据可读:

  • available()仅返回当前输入缓冲区的字节数,无法检测连接关闭(当连接关闭时,read()会返回-1,但available()可能返回0)
  • 这种写法会导致循环无限空转,或者在未正确检测到连接关闭时意外退出(比如缓冲区残留数据)

4. 测试流程逻辑混乱

测试中关闭旧服务器后,又新建了一个SocketServer实例并设置waitForInstruction=false,但未启动线程——这个新实例和客户端的现有连接完全无关,属于无效操作,反而干扰了测试逻辑。同时,未等待旧服务器线程完全终止就执行客户端操作,存在时序问题。


修复方案

1. 修复服务器关闭逻辑及构造函数

public class SocketServer implements Runnable {
    // ... 其他成员变量 ...

    public SocketServer() throws IOException {
        // 修复变量覆盖问题,使用成员变量
        server = new ServerSocket(tcpPort, 0, ipAdress);
        serverRuns = true;
        server.setSoTimeout(timeout);
    }

    public void closeServerSocket() {
        try {
            serverRuns = false;
            // 关闭已建立的客户端连接
            if (client != null && !client.isClosed()) {
                client.close();
            }
            if (server != null && !server.isClosed()) {
                server.close();
            }
        } catch (IOException e) {
            e.printStackTrace();
        }
    }

    // ... 其他方法 ...
}

2. 修复客户端读取逻辑

去掉available()的依赖,直接通过read()检测连接状态:

public void doStuff() throws IOException {
    String request = "Ok?";
    StringBuilder inputStreamString = new StringBuilder();

    super.setSoTimeout(timeOut);
    out = new DataOutputStream(super.getOutputStream());
    in = new DataInputStream(super.getInputStream());

    out.writeBytes(request);
    out.flush(); // 确保数据发送到服务器

    int c;
    while ((c = in.read()) != -1) {
        inputStreamString.append((char) c);
        if (inputStreamString.toString().equals("Ok")) {
            break;
        }
    }

    // 如果read()返回-1,说明连接已关闭
    if (c == -1) {
        throw new IOException("Server connection closed unexpectedly");
    }

    System.out.println(inputStreamString);
}

3. 修正测试流程

移除无效的服务器实例创建,等待旧线程终止后再执行客户端操作:

@Test
public void checkServerisDown() {
    try {
        socketClient.connect();
    } catch (IOException e) {
        fail("IO Error during client connect");
    }

    // 关闭服务器,包括已建立的客户端连接
    testServer.closeServerSocket();
    try {
        // 等待旧服务器线程终止,超时2秒
        monitor.join(2000);
    } catch (InterruptedException e) {
        Thread.currentThread().interrupt();
    }

    System.out.print("Test (1/1) CHECK SERVER IS DOWN.....\n");
    try {
        socketClient.doStuff();
        // 执行到这里说明未抛出预期异常,测试失败
        System.out.println("NOT OK!");
        fail("Expected IOException when server is down");
    } catch (IOException e) {
        // 捕获到预期异常,测试通过
        System.out.println("OK! Caught expected IOException: " + e.getMessage());
    }

    try {
        socketClient.close();
    } catch (IOException e) {
        fail("IO Error closing client socket");
    }
}

内容的提问来源于stack exchange,提问作者hcastro

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.08.18 16:15:32