首页 文章

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

提问于
浏览
3

我正在尝试使用java套接字创建一个客户端服务器游戏 . 我有一个控制游戏逻辑的线程服务器 . 我还有与服务器通信的客户端线程 . 我使用多个客户端处理程序线程来促进服务器到客户端的通信 . 我使用多个线程使用套接字与其他客户端线程进行通信 .

现在,我有一个关于如何促进服务器线程和多个客户端线程之间的通信的问题 . 例如,如果服务器选择要播放的下一个播放器,它应如何通知客户端处理程序线程,然后通过套接字与客户端线程通信?

3 回答

  • 1

    我之前通过以下方式完成了这项工作 . 我有一个服务器套接字

    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中处理它,也可以使用outputstream从服务器发送通知,同样在GameController中 .

    编辑:此外,我应该注意,我的所有通信都是通过XML完成的,客户端或服务器上的控制器解码XML并执行相关请求 .

    希望这可以帮助 . 它确实为我做了工作,并允许我的多人游戏运作良好 .

  • 0

    我怀疑你的客户端线程挂起阻塞读取操作 . 要"release"这些线程并让它们发送数据,你必须通过thread.interrupt()中断它们 . (这会导致阻塞读取抛出InterruptedException . )

    但是,我自己写了一些网络游戏,我真的建议你去研究java.nio packages,特别是 the Selector class . 使用这个类,您可以轻松地使整个服务器成为单线程 . 在同步所有这些客户端线程时,这将为您节省很多麻烦 .

  • 4

    我认为使用像ActiveMQ这样的现有通信基础设施在这里处理低级管道事务非常有用,并且允许您在更高的概念级别处理游戏设计问题,而不是处理低级复杂性 .

    话虽如此 . 如果我理解你,那么你就拥有一个拥有多个线程的游戏客户端,其中一个处理服务器的通信 . 在服务器上,每个客户端和游戏服务器逻辑都有一个通信线程 .

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

相关问题