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

Java多线程无GUI简易聊天服务器:消息重复显示问题求助

解决Java多客户端聊天服务器消息重复显示的问题

嘿,你遇到的这个消息重复显示问题,在多客户端聊天服务器里其实挺常见的,多半是广播逻辑没处理好——要么是服务器把消息回发给了发送方自己,要么是客户端那边重复打印了。我来给你捋捋怎么解决:

问题根源拆解

当客户端发消息到服务器后,正常逻辑是服务器把这条消息转发给其他所有在线客户端。但如果你的广播代码没跳过发送消息的那个客户端,就会导致发送方自己也收到这条消息;再加上如果客户端在发送时已经本地打印了一次,自然就出现重复显示的情况了。

具体解决方案

1. 先把服务器的广播逻辑改对

首先,服务器需要维护一个线程安全的客户端连接集合,每个客户端对应一个处理线程。广播消息的时候,一定要排除发送消息的那个客户端:

public class Server {
    // 用线程安全的集合存所有在线客户端的处理线程
    private static final List<ClientHandler> clientHandlers = new CopyOnWriteArrayList<>();
    private ServerSocket serverSocket;

    public Server() throws IOException {
        serverSocket = new ServerSocket(8888);
        System.out.println("服务器启动成功,等待客户端连接...");

        // 循环监听新的客户端连接
        while (true) {
            Socket clientSocket = serverSocket.accept();
            System.out.println("新客户端接入:" + clientSocket.getInetAddress());
            ClientHandler handler = new ClientHandler(clientSocket);
            clientHandlers.add(handler);
            // 启动客户端处理线程
            new Thread(handler).start();
        }
    }

    // 广播消息的核心方法:传入要排除的客户端,避免回发
    public static void broadcast(String message, ClientHandler sender) {
        for (ClientHandler handler : clientHandlers) {
            // 跳过发送消息的那个客户端
            if (handler != sender) {
                handler.sendMessage(message);
            }
        }
    }

    // 客户端处理线程类
    static class ClientHandler implements Runnable {
        private Socket socket;
        private BufferedReader in;
        private PrintWriter out;

        public ClientHandler(Socket socket) throws IOException {
            this.socket = socket;
            in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
            out = new PrintWriter(socket.getOutputStream(), true);
        }

        @Override
        public void run() {
            String receivedMsg;
            try {
                // 持续读取客户端发送的消息
                while ((receivedMsg = in.readLine()) != null) {
                    System.out.println("收到消息:" + receivedMsg);
                    // 广播给除了自己之外的所有客户端
                    Server.broadcast(receivedMsg, this);
                }
            } catch (IOException e) {
                System.out.println("客户端断开连接");
            } finally {
                // 客户端断开后,从集合里移除
                clientHandlers.remove(this);
                try {
                    socket.close();
                } catch (IOException e) {
                    e.printStackTrace();
                }
            }
        }

        // 给当前客户端发送消息的方法
        public void sendMessage(String message) {
            out.println(message);
        }
    }

    public static void main(String[] args) throws IOException {
        new Server();
    }
}

2. 客户端代码也要配合调整

客户端这边要注意:要么发送消息时本地打印自己的消息,然后只打印服务器转发的其他客户端消息;要么完全依赖服务器转发所有消息(包括自己的),这时候就不要本地提前打印。推荐第一种方式,更符合用户习惯:

public class Client {
    public static void main(String[] args) throws IOException {
        Socket socket = new Socket("localhost", 8888);
        BufferedReader serverIn = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        PrintWriter serverOut = new PrintWriter(socket.getOutputStream(), true);
        BufferedReader consoleInput = new BufferedReader(new InputStreamReader(System.in));

        // 单独开线程接收服务器转发的消息
        new Thread(() -> {
            String msg;
            try {
                while ((msg = serverIn.readLine()) != null) {
                    // 只打印其他客户端发的消息
                    System.out.println("对方:" + msg);
                }
            } catch (IOException e) {
                System.out.println("与服务器断开连接");
            }
        }).start();

        // 读取控制台输入,发送给服务器
        String input;
        while ((input = consoleInput.readLine()) != null) {
            // 本地打印自己发的消息
            System.out.println("我:" + input);
            serverOut.println(input);
        }
    }
}

几个关键提醒

  • 线程安全很重要:用CopyOnWriteArrayList存客户端列表,避免多线程操作时出现并发修改异常。
  • 别漏了移除断开的客户端:客户端断开连接后,一定要从集合里移除它,不然广播时会报错。
  • 打印逻辑要统一:要么自己打印自己的消息,依赖服务器转发别人的;要么所有消息都等服务器转发,别两边都打印,不然肯定重复。

这样调整后,你再启动服务器和两个客户端测试,消息就不会重复显示啦~

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.05.25 04:21:42