如何在Java中促进服务器线程和多个客户端线程之间的通信

3
我正在尝试使用Java套接字创建客户端服务器游戏。我有一个线程服务器,它控制游戏的逻辑。我还有与服务器通信的客户端线程。我使用多个客户端处理程序线程来促进服务器到客户端的通信。我使用多个线程使用套接字与其他客户端线程通信。
现在,我遇到了一个问题,即如何促进服务器线程和多个客户端线程之间的通信。例如,如果服务器需要选择下一个玩家,它应该如何向客户端处理程序线程发出信号,并通过套接字与客户端线程进行通信?
3个回答

4

我以前是通过以下方式完成的。我有一个服务器套接字

    public Server(int port, int numPlayers) {
    game = new PRGameController(numPlayers);

    try {

        MessageOutput.info("Opening port on " + port);
        ServerSocket clientConnectorSocket = new ServerSocket(port);
        MessageOutput.info("Listening for connections");

        while (!game.isFull()) {
            // block until we get a connection from a client
            final Socket client = clientConnectorSocket.accept();
            MessageOutput.info("Client connected from " + client.getInetAddress());

            Runnable runnable = new Runnable() {
                public synchronized void run() {
                    PRGamePlayer player = new PRGamePlayer(client, game);
                }
            };
            new Thread(runnable).start();
        }
    } catch (IOException io) {
        MessageOutput.error("Server Connection Manager Failed...Shutting Down...", io);
        // if the connection manager fails we want to closedown the server
        System.exit(0);
    }
}

然后在客户端,我有类似这样的东西...
public void connect(String ip) {

        try {
            comms = new Socket(ip, 12345);
            comms.setTcpNoDelay(true);
             // get the streams from the socket and wrap them round a ZIP Stream
            // then wrap them around a reader and writer, as we are writing strings
             this.input =  new CompressedInputStream(comms.getInputStream());
             this.output = new CompressedOutputStream(comms.getOutputStream());
             this.connected = true;
             startServerResponseThread();

        } catch (IOException e) {
            ui.displayMessage("Unable to connect to server, please check and try again");
            this.connected = false;
        }

        if (connected) {
            String name = ui.getUserInput("Please choose a player name");
            sendXML(XMLUtil.getXML(new NameSetAction(name, Server.VERSION)));
        }
    }

    /**
    * This method sets up the server response thread. The thread, sits patiently
    * waiting for input from the server, in a seperate thread, so not to hold
    * up any client side activities. When data is recieved from the server
    * it is processed, to perform the appropriate action.
    */
   public void startServerResponseThread() {

      // create the runnable that will be used by the serverListenerThread,
      // to listen for requests from the server
      Runnable runnable = new Runnable() {

         public void run () {

            try {
               // loop forever, or until the server closes the connection
               while (true) {

                  processRequest(input.readCompressedString());
               }
            } catch (SocketException sx) {
               MessageOutput.error("Socket closed, user has shutdown the connection, or network has failed");
            } catch (IOException ex) {
               MessageOutput.error(ex.getMessage(), ex);
            } catch (Exception ex) {
               MessageOutput.error(ex.getMessage(), ex);
            } finally {
               (PRClone.this).connected = false;
               // only shutdown the server if the listener thread has not already been
               // destroyed, otherwise the server will have already been shutdown
               if (serverListenerThread != null) {
                  // shutdown the thread and inform the application the communications has closed
                  MessageOutput.debug("Shutting down server listener Thread");
               }
            }
         }
      };

      // create the thread
      serverListenerThread = new Thread(runnable);
      // start the thread
      serverListenerThread.start();

   }

客户端可以通过输出流向服务器发送请求,并从输入流中读取服务器数据。
服务器可以接受来自客户端的请求,并在GameController中处理它,还可以使用输出流从服务器发送通知,同样在GameController中执行。
编辑:此外,我应该注意到,我所有的通信都是通过XML完成的,客户端或服务器上的控制器解码XML并执行相关的请求。
希望这有所帮助。对于我来说肯定有效,使我的多人游戏运行良好。

我做的事情几乎完全一样! :-) - Jonathan

1

我怀疑您的客户端线程正在阻塞读取操作上挂起。要“释放”这些线程并让它们发送数据,您需要通过thread.interrupt()来中断它们。(这会导致阻塞读取抛出InterruptedException。)

然而,我自己也写过一些网络游戏,我非常建议您研究java.nio,特别是Selector。使用此类,您可以轻松地使整个服务器成为单线程模式。在同步所有这些客户端线程时,这将为您节省很多麻烦。


0

我认为使用像ActiveMQ这样的现有通信基础设施将非常有用,以处理低级管道问题,并允许您在更高的概念层面上解决游戏设计问题,而不是处理低级复杂性。

话虽如此。如果我理解正确,那么您有一个具有多个线程的游戏客户端,其中一个线程处理与服务器的通信。在服务器上,每个客户端和游戏服务器逻辑都有一个通信线程。

我只会使用套接字进行远程通信,并使用队列进行服务器线程之间的通信。在队列中发送不可变对象(或副本),这样您就不需要同步访问消息中的数据。作为同步的基础,您可以在Socket或BlockingQueue上阻塞,然后您就不需要手动同步事物,但是这需要仔细的协议设计。


网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接