#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 внутри цикла, и, похоже, это работает, большое спасибо! Как бы вы поступили по исправлению состояния гонки, поместили бы весь этот цикл в блок синхронизации или есть лучший способ сделать это?