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

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);
            }
        }
    }
}

解决方案

问题根源

  1. 服务端主线程在accept()后仅处理一次输入,阻塞后续客户端连接,无法同时服务多个客户端。
  2. 仅在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();
            }
        }
    }
}

修改说明

  1. 多客户端支持:主线程仅负责接收新连接,每个客户端由独立的ClientHandler线程处理,互不干扰。
  2. 无限次输入接收:ClientHandler内通过while (!socket.isClosed())循环持续读取该客户端的输入,直到连接断开。
  3. 线程安全:用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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.18 18:05:01