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

多线程聊天客户端运行时触发java.net.SocketException: Socket Closed错误排查求助

问题分析与解决方案

首先,你的问题根源出在客户端的Socket被提前关闭了!

看一下Peer类的构造函数:

try (Socket socket = new Socket(hostname, port)){
    // 创建线程并启动
    Thread t1 = new Thread(new SendThread(writer));
    Thread t2 = new Thread(new ReceiveThread(reader));
    t1.start();
    t2.start();
} // 这里try-with-resources会自动关闭Socket!

Java的try-with-resources语法会在代码块执行完毕后,自动调用资源的close()方法。当你启动两个线程后,构造函数里的try块就结束了,Socket被立刻关闭,这时候SendThread和ReceiveThread还在尝试通过这个已关闭的Socket读写数据,自然就会抛出Socket closed异常。

修复方案

1. 调整客户端Socket的生命周期

把Socket的声明移出try-with-resources,改为手动管理Socket的生命周期,确保两个线程运行期间Socket保持打开状态。修改后的Peer类构造函数如下:

public Peer(String hostname, int port){
    this.hostname = hostname;
    this.port = port;
    Socket socket = null;
    try {
        socket = new Socket(hostname, port);
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(socket.getOutputStream()));
        BufferedReader reader = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        Thread t1 = new Thread(new SendThread(writer, socket)); // 传递Socket引用,方便后续主动关闭
        Thread t2 = new Thread(new ReceiveThread(reader, socket));
        t1.start();
        t2.start();
    } catch (UnknownHostException e) {
        e.printStackTrace();
        try {
            if(socket != null) socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        System.exit(-1);
    } catch (IOException e) {
        e.printStackTrace();
        try {
            if(socket != null) socket.close();
        } catch (IOException ex) {
            ex.printStackTrace();
        }
        System.exit(-1);
    }
}

2. 优化线程的关闭逻辑

给SendThread和ReceiveThread添加Socket引用,支持用户主动退出时关闭资源,同时处理服务器断开的情况:

修改SendThread:

public class SendThread implements Runnable{
    private BufferedWriter writer;
    private Socket socket;

    public SendThread(BufferedWriter writer, Socket socket){
        this.writer = writer;
        this.socket = socket;
    }

    @Override
    public void run() {
        Scanner sc = new Scanner(System.in);
        while (true) {
            System.out.print("Enter a String (type 'exit' to quit): ");
            String str = sc.nextLine();
            if ("exit".equals(str)) {
                // 用户主动退出时关闭所有资源
                try {
                    writer.close();
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
                System.exit(0);
            }
            if (str != null){
                try {
                    this.writer.write(str);
                    this.writer.newLine();
                    this.writer.flush();
                } catch (IOException e) {
                    e.printStackTrace();
                    break;
                }
            }
        }
    }
}

修改ReceiveThread:

public class ReceiveThread implements Runnable{
    private BufferedReader reader;
    private Socket socket;

    public ReceiveThread(BufferedReader reader, Socket socket){
        this.reader = reader;
        this.socket = socket;
    }

    @Override
    public void run() {
        while (true){
            String res = null;
            try {
                res = this.reader.readLine();
                if (res != null){
                    System.out.println("Server response: "+ res);
                } else {
                    // 服务器断开连接时,关闭本地Socket
                    socket.close();
                    break;
                }
            } catch (IOException e) {
                // 忽略Socket主动关闭的异常,直接结束线程
                if (!(e instanceof SocketException && "Socket closed".equals(e.getMessage()))) {
                    e.printStackTrace();
                    System.exit(-1);
                }
                break;
            }
        }
    }
}

3. 修复服务器端的广播问题

服务器的broadcast方法每次都用try-with-resources创建BufferedWriter,会导致客户端的输出流被关闭,后续无法接收消息,需要调整:

public synchronized void broadcast(String message) throws IOException {
    Iterator<Socket> iterator = clients.iterator();
    while (iterator.hasNext()) {
        Socket client = iterator.next();
        if (client.isClosed()) {
            iterator.remove(); // 移除已断开的客户端
            continue;
        }
        BufferedWriter writer = new BufferedWriter(new OutputStreamWriter(client.getOutputStream()));
        writer.write(message);
        writer.newLine();
        writer.flush();
        // 不要关闭writer,否则客户端的输出流会被永久关闭
    }
}

这样修改后,客户端的Socket不会被提前关闭,也能正常接收服务器的广播消息了。

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.04.29 16:07:29