0

我正在编写一个多人游戏,并且正在使用套接字在 Java 中为它编写自己的服务器。

我使用 Java 教程和一些谷歌搜索成功地启动并运行了一个服务器,但是当我尝试实现一个多线程服务器来处理多个客户端时,它工作不正常。

第一个连接的客户端仍然可以正常工作,但任何其他连接的客户端在将输入发送到服务器后只会坐在那里,什么也不做。这是我的代码:

//This class handled Server/client communication for one client.
public class GameRoom implements Runnable {
  public GameRoom(Socket socket) {
    this.socket = socket;
  }

  public void run() {
    logger.info("Game room has been created.");

    while(true) {
      try {
        in = new BufferedReader(new InputStreamReader(socket.getInputStream()));
        out = new PrintWriter(socket.getOutputStream());
        String clientResponse = in.readLine();
        out.println("You wrote " + clientResponse);
        out.flush();
      } catch (IOException e) {
        logger.severe(e.getMessage());
        throw new RuntimeException();
      }
    }
  }

  //Will eventually change the string to a GSON object and delegate the appropriate actions based on the gson object type.
  private String delegateClientInput(String clientInput) {
    return "I heard you say: " + clientInput + "\n";
  }

  private BufferedReader in;
  private PrintWriter out;
  private Socket socket;
  private static final Logger logger = LogUtil.initializeServerLog(GameRoom.class.toString());
}

/*
 * This class houses the server socket itself.  Handles connecting to multiple clients.
 */
public class ServerClientThreadPool extends Thread {
  public static void main(String[] args) {
    ServerClientThreadPool serverClientThreadPool = new ServerClientThreadPool();
    serverClientThreadPool.startServer();
  }

  ServerClientThreadPool() {
    try {
      serverListener = new ServerSocket(GAME_ROOM_PORT);
    } catch (IOException e) {
      logger.severe(e.getMessage());
      throw new RuntimeException();
    }
  }

  public void startServer() {
    for(int i = 0; i < MAX_CONNECTIONS; i++) {
      try {
        GameRoom gameRoom = new GameRoom(serverListener.accept());
        gameRoom.run();
      } catch (IOException e) {
        logger.severe(e.getMessage());
        throw new RuntimeException();
      }
    }
  }

  private static final int GAME_ROOM_PORT = 18000;
  private ServerSocket serverListener;
  private static final int MAX_CONNECTIONS = 100;
  private static final Logger logger = LogUtil.initializeServerLog(ServerClientThreadPool.class.getName());
}

这是客户端的主要功能,当然位于一个单独的程序中:

ClientSocketWrapper clientSocketWrapper = ClientSocketWrapper.createSocketWrapperAndConnectTo("localhost", 18000);

/** MAIN ENTRY POINT FOR CLIENT */
while(true) {
  clientSocketWrapper.updateInputOutputStream();
  clientSocketWrapper.writeToOutputStream(executeClientInputProcessing());
  updateGameState(clientSocketWrapper.getServerResponse());
}

我意识到您无法真正看到这些方法内部发生了什么,但它基本上只是像 Java 教程那样实现客户端并且它按预期工作。如果您认为我需要发布此处运行的方法,请告诉我,但我认为问题出在服务器端。

奖金问题:我也想知道我是否在正确的轨道上。游戏相当复杂,但我只是打算使用 Gson 来序列化和反序列化对象,并根据来回发送的 gson 字符串委托适当的操作。在谷歌搜索服务器/客户端架构之后,我的方法似乎太简单了。我很难找到好的资源来学习更高级的服务器/客户端架构,所以任何指向优秀书籍或教程的链接都会有很大帮助。

谢谢大家!

4

1 回答 1

3

你不想gameRoom.run()直接打电话。这会导致它在当前线程中执行,这会占用您的服务器循环,直到该run()方法完成。相反,您应该在单独的线程中运行它。从 Executors 工厂类获得的ExecutorService一种简单的处理方式。例如,在您的 ServerClientThreadPool 中创建一个 ExecutorService:

public class ServerClientThreadPool {
    private ExecutorService executorService = Executors.newCachedThreadPool();

然后在接受socket连接后将gameRoom提交给服务:

GameRoom gameRoom = new GameRoom(serverListener.accept());
executorService.submit(gameRoom);

请注意,我没有extends Thread在上面的 ServerClientThreadPool 上写。在上面的代码中,您没有以任何方式将它用作线程,除非您需要服务器在其他事情发生的同时接受连接(除了游戏之外),否则这样做可能是不正确的房间,这将在单独的线程中处理)。如果您确实需要它,那么它也应该是 a Runnable,而不是 a Thread,并且还应该提交给 a ExecutorService

基于您的代码中关于线程如何工作的明显混淆,我建议您重新访问Java“并发”教程,其中包括关于 Executors 的部分

奖励:我认为来回传递 JSON 是一种合理的通信方式。这样做意味着您并不过分关注性能,因此我建议您停止自己处理套接字,而是在服务器端使用嵌入式 HTTP 服务器,在客户端使用简单的 HTTP 客户端。这将为您处理所有套接字和线程的废话,让您不必担心游戏逻辑的细节。

例如,嵌入 Jetty是一项微不足道的工作,并且有许多功能强大且简洁的 HTTP/REST 客户端,例如Apache Http ClientJersey REST Client

于 2013-02-23T19:05:47.603 回答