Java - 多线程服务器仅适用于一个连接

标签 java multithreading sockets client-server

我正在编写一个多人游戏,并且正在使用套接字用 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 字符串委托(delegate)适当的操作。在谷歌搜索服务器/客户端架构之后,我的方法似乎太简单了。我很难找到好的资源来学习更高级的服务器/客户端架构,因此任何优秀书籍或教程的链接都会有很大帮助。

谢谢大家!

最佳答案

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

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

接受套接字连接后将gameRoom提交给服务:

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

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

基于您的代码中关于线程如何工作的明显困惑,我建议您重新访问 Java "Concurrency" tutorial ,其中包括a section on Executors .

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

例如,embedding Jetty是一项微不足道的任务,并且有许多强大且简洁的 HTTP/REST 客户端,例如 Apache Http ClientJersey REST Client .

关于Java - 多线程服务器仅适用于一个连接,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/15044384/

相关文章:

java - 当我尝试创建图像文件时,try-catch 没有捕获 IOException

java - 如何向 GUI 添加计数器

java - 使用 spring 3 注释发送成功或错误消息以查看

android - 你如何知道 Linux 中是否已经存在互斥对象?

c++ - 多线程写入文件C++

c++ - 从 GetQueuedCompletionStatus 解除阻止

MAC OSX 上的 Python IPv6 多播错误

java - 防止在 SessionScoped beans 中序列化 CDI 注入(inject)的 Logger

java - 哪种线程实现方式最有利?

c - 在c中使用socket选择阻塞