Java Socket游戏服务端如何等待所有客户端输入用户名后再广播消息
解决方案
调整思路
- 移除原有连接数达标立即广播的逻辑,避免在玩家未完成用户名输入时提前发送开局消息
- 在GameServer中新增线程安全的就绪玩家计数器,用于统计完成用户名输入的玩家数量
- 每个玩家完成用户名输入后更新就绪计数,当计数达到最大玩家数阈值时,统一触发开局广播
- 新增开局状态标记,避免多线程并发时重复触发广播
代码修改
1. GameServer.java 调整
首先新增2个成员变量,删除accept循环中的广播逻辑,新增玩家就绪回调方法:
import java.util.concurrent.atomic.AtomicInteger; public class GameServer { // 原有成员变量保留:serverSocket、maxPlayers、playerThreads等 private AtomicInteger readyPlayerCount = new AtomicInteger(0); private boolean gameStarted = false; // 原有其他方法保留,修改accept循环逻辑: public void start() throws IOException { while (true) { Socket clientSocket = serverSocket.accept(); PlayerThread playerThread = new PlayerThread(clientSocket, this); playerThreads.add(playerThread); playerThread.start(); // 删掉原来此处的连接数达标广播逻辑 } } // 新增玩家就绪回调方法 public synchronized void onPlayerReady() { int currentReady = readyPlayerCount.incrementAndGet(); // 仅当所有玩家就绪且未开局时,触发广播 if (currentReady == maxPlayers && !gameStarted) { gameStarted = true; broadcast("All players have joined! Let the game begin!", null); } } // 原有broadcast、addPlayer等方法保留 }
2. PlayerThread.java 调整
修正原有变量名笔误,在玩家完成用户名输入后调用就绪回调:
@Override public void run() { try { BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream())); out = new PrintWriter(socket.getOutputStream(), true); out.println("What's your name? "); String userName = in.readLine(); Player player = new Player(userName); gameServer.addPlayer(player); // 玩家输入完成,通知服务端已就绪 gameServer.onPlayerReady(); String clientMove; while ((clientMove = in.readLine()) != null) { gameServer.makeMove(player, clientMove); gameServer.broadcast(player.getName() + " made the move: " + clientMove, this); } } catch (IOException e) { e.printStackTrace(); } }
说明
- 使用
AtomicInteger做就绪计数,保证多线程并发操作时计数的准确性,不会出现计数丢失问题 onPlayerReady方法加synchronized同步锁,配合gameStarted标志位,保证开局消息只会广播一次,不会出现重复发送的问题- 原有广播、玩家移动处理等逻辑无需修改,不影响后续游戏流程
内容的提问来源于stack exchange,提问作者user16037629
相关产品推荐
相关产品推荐

