Java服务端Socket改造:支持多客户端连接并持续接收请求
问题描述
我实现了一个Socket服务端,目标是让它持续接收多个不同客户端的输入,并且每个客户端可以发送无限次请求。但实际运行后发现,即便客户端每秒发送新输入,服务端也只能打印单次输入。现在需要修改代码实现以下两点:
- 同时服务多个客户端
- 接收并打印每个客户端的无限次输入
原服务端Socket线程代码
public void run() { try { System.out.println("Binding socket.."); ServerSocket serverSocket = new ServerSocket(portNumber); // THE MAIN READING LOOP: while(true){ Socket socket = serverSocket.accept(); System.out.println("Socket is listening.."); DataInputStream dis = new DataInputStream(socket.getInputStream()); //here, the code is meant to read the inputs made in the last 2 seconds as a single block (I'm making a game) if(tickHappened){ DebugLogger.print("Tick happened."); String undividedInputs = dis.readUTF(); String[] inputs = undividedInputs.split("/"); DebugLogger.print("Messages:"); for(int i =0; i < inputs.length;i++){ System.out.println(inputs[i]); } tickHappened = false; } } } catch (IOException e) { System.err.println(e); throw new RuntimeException(e); } }
原客户端代码(每秒发送输入,调用sendMessageToServer方法)
package Graphics; import java.io.DataOutputStream; import java.io.IOException; import java.net.ServerSocket; import java.net.Socket; public class ClientToServerCommunications { private static Socket s; static { try { s = new Socket("localhost",6666); } catch (IOException e) { throw new RuntimeException(e); } } ; private static DataOutputStream dout; static { try { dout = new DataOutputStream(s.getOutputStream()); } catch (IOException e) { throw new RuntimeException(e); } } ; public static void sendMessageToServer(String message) throws InterruptedException { while (true) { Thread.sleep(1000); try { dout.writeUTF(message); } catch (Exception e) { System.out.println(e); } } } }
解决方案
问题根源
- 服务端主线程在
accept()后仅处理一次输入,阻塞后续客户端连接,无法同时服务多个客户端。 - 仅在
tickHappened为true时读取一次数据,没有循环接收客户端的持续输入。
修改思路
- 为每个新连接的客户端创建独立线程,实现多客户端并行处理。
- 在客户端线程内循环读取输入,直到连接断开,实现无限次接收。
- 保留游戏
tick逻辑,在每个客户端线程中处理每2秒的输入块。
修改后的服务端代码
public void run() { try { System.out.println("Binding socket.."); ServerSocket serverSocket = new ServerSocket(portNumber); // 主线程循环监听新客户端连接 while(true){ Socket socket = serverSocket.accept(); System.out.println("新客户端连接: " + socket.getInetAddress()); // 为每个客户端启动独立处理线程 new ClientHandler(socket).start(); } } catch (IOException e) { System.err.println(e); throw new RuntimeException(e); } } // 独立的客户端处理线程类 private static class ClientHandler extends Thread { private Socket socket; private DataInputStream dis; // 确保tickHappened在多线程下可见 private volatile boolean tickHappened; public ClientHandler(Socket socket) throws IOException { this.socket = socket; this.dis = new DataInputStream(socket.getInputStream()); // 这里可以添加定时触发tick的逻辑,比如用ScheduledExecutorService每2秒设置tickHappened为true } @Override public void run() { try { // 持续监听该客户端输入,直到连接关闭 while (!socket.isClosed()) { // 等待tick触发 while (!tickHappened) { Thread.sleep(100); // 避免空循环占用CPU } DebugLogger.print("Tick happened. 读取客户端[" + socket.getInetAddress() + "]的输入"); String undividedInputs = dis.readUTF(); String[] inputs = undividedInputs.split("/"); DebugLogger.print("Messages:"); for(String input : inputs){ System.out.println(input); } tickHappened = false; } } catch (IOException | InterruptedException e) { System.err.println("客户端[" + socket.getInetAddress() + "]连接断开: " + e.getMessage()); } finally { // 关闭资源 try { dis.close(); socket.close(); } catch (IOException e) { e.printStackTrace(); } } } }
修改说明
- 多客户端支持:主线程仅负责接收新连接,每个客户端由独立的
ClientHandler线程处理,互不干扰。 - 无限次输入接收:
ClientHandler内通过while (!socket.isClosed())循环持续读取该客户端的输入,直到连接断开。 - 线程安全:用
volatile修饰tickHappened,确保多线程下的状态可见性,避免线程感知不到tick触发的问题。
客户端代码优化(可选)
原客户端静态块初始化异常无法优雅处理,修改为构造方法初始化:
package Graphics; import java.io.DataOutputStream; import java.io.IOException; import java.net.Socket; public class ClientToServerCommunications { private Socket s; private DataOutputStream dout; public ClientToServerCommunications() throws IOException { this.s = new Socket("localhost", 6666); this.dout = new DataOutputStream(s.getOutputStream()); } public void sendMessageToServer(String message) throws InterruptedException { while (!s.isClosed()) { Thread.sleep(1000); try { dout.writeUTF(message); dout.flush(); // 确保数据立即发送 } catch (IOException e) { System.err.println("发送失败: " + e.getMessage()); break; } } } }
额外注意事项
- 定时触发
tickHappened的逻辑需线程安全,比如使用ScheduledExecutorService每2秒设置为true。 - 生产环境需处理客户端异常断开的情况,及时关闭资源避免内存泄漏。
内容的提问来源于stack exchange,提问作者Andrew Kor
相关产品推荐
相关产品推荐

