Настройка многопоточного Java-сервера

#java #multithreading

#java #многопоточность

Вопрос:

Я пытаюсь написать игровой сервер, который поддерживает мультиплеер по сети.

Чего я пытаюсь достичь, так это сервера, который в цикле прослушивает соединения и настраивает поток для каждого клиента. это должно продолжаться до тех пор, пока хост игры не нажмет кнопку «Запустить игру», после чего он должен продолжить работу с игровой логикой, отправкой данных и т.д.

Для достижения этой цели я использую этот код в методе run этого потока для зацикливания и прослушивания соединений.

 try {
  while (gameStarted == false) {
      System.out.print(gameStarted);

      clientSocket = ss.accept();
      ClientThreadOnServer runClient = new ClientThreadOnServer(clientSocket);
      clientThreads.add(runClient);
      window.updateText();
  }
  System.out.println("end");
  

Затем на моем сервере я разместил выполняемый внутренний класс, который выводит JFrame со счетчиком и кнопкой запуска, идея которого заключается в том, что логическое значение gameStarted переключается, когда пользователь нажимает «Запустить игру» во фрейме, вот так:

 public void actionPerformed(ActionEvent e) {
  if (e.getActionCommand().equals("start")) {
    synchronized (this) {
      System.out.println("click");
      gameStarted = true;
    }
  }
}
  

однако это не работает! При нажатии кнопки start кнопка обнаруживается и нажимается, поэтому предположительно логическое значение изменяется, но сервер не выходит из цикла while и не продолжает работу. Думаю, я знаю почему, цикл выполняется в ss.accept(); и не проверяет логическое условие, как я надеялся.

Я даже не совсем уверен, правильно ли я это делаю, особенно в отношении списка клиентских потоков на сервере, который я надеюсь затем использовать для управления отправкой данных. Если я делаю это совершенно неправильным способом, пожалуйста, дайте мне знать, как это должно быть сделано, или, если я не сумасшедший, как мне заставить мой код работать?

Комментарии:

1. while (gameStarted == false) { -> while (!gameStarted) { Не ответ, просто вопрос стиля.

2. К сожалению, это не имеет никакого значения, хотя

3. Извините, я понял, что мой комментарий был не совсем ясен. И вы ответили до того, как я его отредактировал.

Ответ №1:

accept()
Прослушивает соединение, которое должно быть установлено к этому сокету, и принимает его. Метод блокируется до тех пор, пока не будет установлено соединение.

Это означает, что цикл будет проверять состояние игры только сразу после подключения клиента. Вы можете использовать setTimeout() , чтобы указать, на какой срок может блокироваться вызов accept, прежде чем выдавать SocketTimeoutException , чтобы указать, что клиент не подключался. Вы можете использовать это в своем цикле, чтобы заставить accept() не блокировать навсегда и позволить вам проверять gameStarted снова.

Примечание:
Выполняется не полная синхронизация при gameStarted , а только при записи. Это означает, что существует условие гонки, и вы можете попытаться принять дополнительный клиент после нажатия кнопки «Пуск». Самый простой способ исправить это — следующий. Вы не хотите помещать весь цикл в синхронизированный блок, потому что тогда кнопка «Пуск» никогда не сможет войти в синхронизированный блок, потому что цикл удерживает блокировку.

 private synchronized boolean isGameStarted() {
  return gameStarted;
}

...
try {
  while (!isGameStarted()) {
...
  

Комментарии:

1. Возможно, вы захотите изменить setTimeout() на setSoTimeout(). Ссылка правильная, но текст нет, что может вызвать некоторую путаницу. Я бы сделал это, но правка неавтора должна содержать не менее шести символов.

2. Итак, я выполнил ss.setSoTimeout(1000); в конструкторе исправил catch и сделал try / catch внутри цикла, и, похоже, это работает, большое спасибо! Как бы вы поступили по исправлению состояния гонки, поместили бы весь этот цикл в блок синхронизации или есть лучший способ сделать это?