Нить висит на розетке напиши: почему? и как это предотвратить? - PullRequest
1 голос
/ 07 апреля 2020

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

Все работает отлично, даже в выходные дни, когда легко подключается более 500 подключенных игроков. в то же время. Это означает более 500 потоков, что, по-видимому, в большинстве случаев не вызывает серьезных проблем. Нет избыточной загрузки ЦП или пропускной способности сети.

За исключением одного крупного случая, когда один игрок, имеющий фиктивное соединение, может заблокировать всю систему. Регулярно случается, что поток остается заблокированным в socketOutputStream.write в течение длительного времени, возможно, нескольких часов, если я не пытаюсь прервать его или принудительно закрыть соответствующий сокет.

Hance, оттуда два вопросы:

Вопрос 1. В каком случае операция записи в сокет может блокироваться?

Если для операций чтения очевидно, что она блокируется, пока нет новых данных для чтения, я не понимаю Я не очень хорошо понимаю, почему запись может блокироваться, если я не пытаюсь отправить больше данных, чем может обработать все мое сетевое соединение. В этом последнем случае очевидно, что мне нужно подождать и заблокировать некоторое время ... но это далеко не так. У меня ограничение пропускной способности 200 Мбит / с, и я едва достигаю 1 или 2 Мбит / с при ru sh часах. Обмен данными очень минимален: ни звука, ни изображений, ни видео не просматривается во время игры; только простые текстовые команды.

Вопрос 2: В классе Socket есть метод setSOTimeout (см. do c в https://docs.oracle.com/en/java/javase/11/docs/api/java.base/java/net/Socket.html), который устанавливает максимальное время ожидания для операций чтения. Если сокет ничего не читает в течение тайм-аута, запускается специальное исключение c, которое дает мне возможность проверить, все еще ли соединение активно и отвечает. Я уже использую этот механизм, чтобы выгнать не отвечающих игроков на основе этого. Однако он применяется только к операциям чтения .

Есть ли что-то похожее для операций записи? Я ничего не могу найти.

Некоторые дополнительные значения точности:

  • Сервер работает на компьютере linux с Java AdoptOpenJDK 11.0.6
  • Игроки могут подключаться через SSL или нет (ServerSocket и SSLServerSocket прослушивают разные порты). Большинство из них (60%) используют SSL. Я использую стандартную конфигурацию SSL с сертификатом letsencrypt. Использование SSL не кажется медленнее, чем не-SSL-соединения.
  • Кажется, у меня не было этой проблемы с Java 8. У меня она есть, так как я обновил с 8 до 11.

Спасибо за ваши ответы.

1 Ответ

0 голосов
/ 08 апреля 2020

Запись в сокет может блокироваться, если вы продолжаете отправлять данные, но другая сторона отказывается их читать.

Операционная система поддерживает буфер для исходящих данных на случай, если данные будут потеряны и должны быть отправлены. снова. Данные удаляются из буфера, когда другая сторона подтверждает их получение («подтверждение»). Если другая сторона перестает подтверждать данные, но вы продолжаете отправлять, в конце концов буфер заполняется, и дальнейшие попытки записи блокируют, чтобы данные не были потеряны.

Нет настройки времени ожидания для записи в сокеты, которые я ' Я в курсе. Возможно, вы могли бы добавить сообщение «вы все еще там» в протокол вашего приложения и закрыть соединение, если не получили ответ. Другой вариант - перейти от блокирования ввода-вывода сокета к неблокирующему, но это серьезная задача.

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