Java TCP Socket服务端如何实现单条消息同时发送给所有连接客户端
问题根源
- 你当前的代码将控制台输入读取逻辑
Scanner.nextLine()写在了每个ClientHandler线程内部,每个连接的客户端对应的线程都会独立阻塞等待控制台输入,因此N个客户端连接时就需要输入N次才能分别触发每个线程的发送逻辑 - 没有全局维护已连接客户端的输出流列表,无法做到单条消息批量推送
修改方案
调整服务端架构逻辑:
- 新增线程安全的全局集合存储所有已连接客户端的输出流
- 将控制台输入读取逻辑移到服务端独立线程,读取到消息后遍历集合批量推送
- ClientHandler仅负责客户端连接生命周期管理:连接建立时将输出流加入集合、连接断开时从集合移除输出流
修改后完整服务端代码
import java.io.*; import java.net.*; import java.util.*; import java.util.concurrent.CopyOnWriteArrayList; // Server class class Server { public static class countLogic { public static int client_count = 0; } // 全局线程安全集合,存储所有在线客户端的输出流 private static final CopyOnWriteArrayList<PrintWriter> clientWriters = new CopyOnWriteArrayList<>(); public static void main(String[] args) { System.out.println("[SERVER]"); ServerSocket server = null; try { server = new ServerSocket(1234); server.setReuseAddress(true); // 启动单独线程处理控制台输入群发 new Thread(() -> { Scanner sc = new Scanner(System.in); while (true) { String msg = sc.nextLine(); // 遍历所有客户端输出流群发消息 for (PrintWriter writer : clientWriters) { writer.println(msg); } } }).start(); // 主线程持续监听客户端连接 while (true) { Socket client = server.accept(); countLogic.client_count++; System.out.println("Client ("+countLogic.client_count+") connected: " + client.getInetAddress().getHostAddress()); ClientHandler clientSock = new ClientHandler(client); new Thread(clientSock).start(); } } catch (IOException e) { e.printStackTrace(); } finally { if (server != null) { try { server.close(); } catch (IOException e) { e.printStackTrace(); } } } } // ClientHandler类仅负责连接生命周期管理 private static class ClientHandler implements Runnable { private final Socket clientSocket; public ClientHandler(Socket socket) { this.clientSocket = socket; } public void run() { PrintWriter out = null; BufferedReader in = null; try { out = new PrintWriter(clientSocket.getOutputStream(), true); in = new BufferedReader(new InputStreamReader(clientSocket.getInputStream())); // 输出流加入全局集合 clientWriters.add(out); // 阻塞监听客户端消息,感知客户端断开事件 String clientMsg; while ((clientMsg = in.readLine()) != null) { // 若需要处理客户端上行消息可在这里补充逻辑 } } catch (IOException e) { System.out.println("Client disconnected: " + clientSocket.getInetAddress().getHostAddress()); } finally { // 连接断开时移除输出流 if (out != null) { clientWriters.remove(out); out.close(); } try { if (in != null) { in.close(); clientSocket.close(); } } catch (IOException e) { e.printStackTrace(); } } } } }
客户端代码优化(可选)
你当前客户端代码的循环判断逻辑存在小瑕疵,line初始为null,第一次判断!"exit".equalsIgnoreCase(line)永远为true,不影响正常运行,可调整为更严谨的写法:
import java.io.*; import java.net.*; // Client class class Client { public static void main(String[] args) { // 支持从启动参数传入客户端标识,多开测试时更容易区分 String clientName = args.length > 0 ? args[0] : "CLIENT 1"; System.out.println("["+clientName+"]"); try (Socket socket = new Socket("localhost", 1234)) { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); String line; while ((line = in.readLine()) != null && !"exit".equalsIgnoreCase(line)) { System.out.println("Server: " + line); } } catch (IOException e) { e.printStackTrace(); } } }
内容的提问来源于stack exchange,提问作者user17382179
相关产品推荐
相关产品推荐

