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

Java TCP Socket服务端如何实现单条消息同时发送给所有连接客户端

问题根源
  • 你当前的代码将控制台输入读取逻辑Scanner.nextLine()写在了每个ClientHandler线程内部,每个连接的客户端对应的线程都会独立阻塞等待控制台输入,因此N个客户端连接时就需要输入N次才能分别触发每个线程的发送逻辑
  • 没有全局维护已连接客户端的输出流列表,无法做到单条消息批量推送
修改方案

调整服务端架构逻辑:

  1. 新增线程安全的全局集合存储所有已连接客户端的输出流
  2. 将控制台输入读取逻辑移到服务端独立线程,读取到消息后遍历集合批量推送
  3. 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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.09.26 18:24:04