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
相关产品推荐
相关产品推荐

