Socket多线程聊天服务器问题:客户端无法互相通信
嘿,我帮你搞定这个问题!你现在的核心问题是服务器没有实现消息的广播转发逻辑——当一个客户端发送消息过来时,服务器只处理了和这个客户端的单向交互,没有把消息同步给列表里的其他在线客户端。下面我给你一套完整的修复方案,包括修正后的服务器代码和客户端示例:
核心问题分析
你原来的代码应该是只维护了客户端连接的列表,但没有在收到消息时遍历列表,把消息转发给除发送者之外的其他客户端。另外,普通的ArrayList在多线程环境下会有并发修改异常,得用线程安全的集合来管理客户端连接。
修正后的聊天服务器代码
1. ChatServer 主类(负责监听连接、管理客户端)
package Threads.tcp; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; import java.util.concurrent.CopyOnWriteArrayList; public class ChatServer { private static final int PORT = 10023; // 用CopyOnWriteArrayList保证多线程下的操作安全,避免并发修改异常 private static final CopyOnWriteArrayList<ClientHandler> clientHandlers = new CopyOnWriteArrayList<>(); public static void main(String[] args) { try (ServerSocket serverSocket = new ServerSocket(PORT)) { System.out.println("聊天服务器已启动,监听端口:" + PORT); // 持续监听客户端连接请求 while (true) { Socket clientSocket = serverSocket.accept(); System.out.println("新客户端上线:" + clientSocket.getInetAddress().getHostAddress()); // 为每个客户端创建独立的处理线程 ClientHandler handler = new ClientHandler(clientSocket); clientHandlers.add(handler); new Thread(handler).start(); } } catch (IOException e) { System.err.println("服务器启动失败:" + e.getMessage()); e.printStackTrace(); } } // 广播消息给所有在线客户端(排除消息发送者) public static void broadcastMessage(String message, ClientHandler sender) { for (ClientHandler handler : clientHandlers) { if (handler != sender) { handler.sendMessage(message); } } } // 移除断开连接的客户端 public static void removeClient(ClientHandler handler) { clientHandlers.remove(handler); System.out.println("客户端下线,当前在线人数:" + clientHandlers.size()); } }
2. ClientHandler 类(负责单个客户端的消息读写)
这个类会单独跑在一个线程里,处理和对应客户端的通信,同时调用服务器的广播方法转发消息:
package Threads.tcp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; public class ClientHandler implements Runnable { private final Socket clientSocket; private PrintWriter messageWriter; private BufferedReader messageReader; public ClientHandler(Socket socket) { this.clientSocket = socket; } @Override public void run() { try { // 初始化输入输出流,用于和客户端收发消息 messageReader = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); messageWriter = new PrintWriter(new OutputStreamWriter(clientSocket.getOutputStream()), true); String receivedMessage; // 持续读取客户端发送的消息 while ((receivedMessage = messageReader.readLine()) != null) { System.out.println("收到消息:" + receivedMessage); // 调用服务器的广播方法,把消息转发给其他客户端 ChatServer.broadcastMessage(receivedMessage, this); } } catch (IOException e) { System.out.println("与客户端通信中断:" + e.getMessage()); } finally { // 关闭资源并从服务器列表中移除当前客户端 try { messageReader.close(); messageWriter.close(); clientSocket.close(); } catch (IOException e) { e.printStackTrace(); } ChatServer.removeClient(this); } } // 发送消息给当前客户端 public void sendMessage(String message) { messageWriter.println(message); } }
配套的客户端代码(解决客户端接收阻塞问题)
客户端需要单独开一个线程处理服务器发来的消息,不然会因为等待输入而阻塞接收逻辑:
package Threads.tcp; import java.io.BufferedReader; import java.io.IOException; import java.io.InputStreamReader; import java.io.OutputStreamWriter; import java.io.PrintWriter; import java.net.Socket; import java.util.Scanner; public class ChatClient { private static final String SERVER_HOST = "localhost"; private static final int SERVER_PORT = 10023; public static void main(String[] args) { try (Socket socket = new Socket(SERVER_HOST, SERVER_PORT)) { System.out.println("成功连接到聊天服务器!"); // 启动独立线程接收服务器转发的消息 new Thread(() -> { try (BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()))) { String serverMessage; while ((serverMessage = in.readLine()) != null) { // 收到消息时打印,同时提示用户继续输入 System.out.println("\n[其他客户端]:" + serverMessage); System.out.print("请输入你的消息:"); } } catch (IOException e) { System.out.println("\n与服务器断开连接"); } }).start(); // 处理用户输入,发送消息到服务器 PrintWriter out = new PrintWriter(new OutputStreamWriter(socket.getOutputStream()), true); Scanner scanner = new Scanner(System.in); while (true) { System.out.print("请输入你的消息:"); String userInput = scanner.nextLine(); out.println(userInput); } } catch (IOException e) { System.err.println("无法连接到服务器:" + e.getMessage()); e.printStackTrace(); } } }
关键注意点
- 线程安全集合:用
CopyOnWriteArrayList管理客户端,避免多线程添加/移除客户端时出现并发修改异常。 - 独立线程处理客户端:每个客户端连接对应一个线程,服务器可以同时处理多个客户端的请求,不会阻塞。
- 广播逻辑:
broadcastMessage方法确保消息只发给除发送者外的其他客户端,避免自己收到自己发的消息。 - 资源清理:客户端断开时及时关闭流和Socket,并从服务器列表中移除,避免无效连接占用资源。
内容的提问来源于stack exchange,提问作者user9064113
相关产品推荐
相关产品推荐

