Как облегчить связь между потоком сервера и несколькими потоками клиента в Java - PullRequest
3 голосов
/ 03 октября 2010

Я пытаюсь создать клиент-серверную игру с использованием сокетов Java. У меня есть сервер потоков, который контролирует логику игры. У меня также есть клиентские потоки, которые общаются с сервером. Я использую несколько потоков обработчиков клиента для облегчения взаимодействия между сервером. Я использую несколько потоков для связи с другими потоками клиента с помощью сокетов.

Теперь у меня проблема с тем, как упростить связь между потоком сервера и несколькими потоками клиента. Например, должен ли сервер выбрать следующего игрока для игры, как он должен сигнализировать потоку обработчика клиента и, в свою очередь, обмениваться данными с потоком клиента через сокеты?

Ответы [ 3 ]

4 голосов
/ 03 октября 2010

Я делал это раньше следующим образом. У меня есть сокет сервера

    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, а также отправлять уведомления с сервера с помощью outputtream, снова в GameController.

РЕДАКТИРОВАТЬ: Кроме того, я должен отметить, что все мое общение осуществляется через XML, и контроллер на клиенте или сервере декодирует XML и выполняет соответствующий запрос.

Надеюсь, это поможет. Это, безусловно, делает работу для меня, и позволяет моим многопользовательским играм хорошо работать.

1 голос
/ 03 октября 2010

Я подозреваю, что ваши клиентские потоки зависают при блокирующей операции чтения.Чтобы «освободить» эти потоки и заставить их отправлять данные вместо этого, вам нужно прервать их через thread.interrupt ().(Это может привести к тому, что чтение с блокировкой вызовет исключение InterruptedException.)

Однако я сам написал несколько сетевых игр и действительно рекомендую вам заглянуть в java.nio пакеты и особенно Selector класс .Используя этот класс, вы можете легко сделать весь сервер однопоточным.Это сэкономит вам много головных болей, когда дело доходит до синхронизации всех этих клиентских потоков.

0 голосов
/ 03 октября 2010

Я думаю, что использование существующей коммуникационной инфраструктуры, такой как ActiveMQ, было бы очень полезно для решения низкоуровневых задач, связанных с конвейером, и позволяло бы вам решать проблемы игрового дизайна на более высоком концептуальном уровне, а не заниматься низкоуровневыми сложностями.

Это, как говорится.Если я вас понял, то у вас есть игровой клиент с несколькими потоками, один из которых связан с коммуникациями с сервером.На сервере есть поток сообщений для каждого клиента и логика игрового сервера.

Я бы использовал сокеты только для удаленной связи и очереди для связи между потоками сервера.В очереди отправляйте неизменные объекты (или копии) туда и обратно, чтобы вам не нужно было синхронизировать доступ к данным в сообщениях.В качестве основы для синхронизации вы можете заблокировать Socket или BlockingQueue, тогда вам не нужно вручную синхронизировать объекты, однако это требует тщательного проектирования протокола.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...