Подтвердить что ты не робот

API сокетов Java. Как узнать, было ли соединение закрыто?

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

Вызов isConnected() в сокет, который был удален удаленно, всегда возвращает true. Точно так же вызов isClosed() в сокет, который был удален удаленно, всегда возвращает false. Я прочитал, что для фактического определения того, был ли сокет закрыт, данные должны быть записаны в выходной поток, и исключение должно быть уловлено. Это похоже на действительно нечистый способ справиться с этой ситуацией. Нам просто приходилось спамить сообщение о мусоре по сети, чтобы знать, когда сокет закрылся.

Есть ли другое решение?

4b9b3361

Ответ 1

Нет API-интерфейса TCP, который сообщит вам о текущем состоянии соединения. isConnected() и isClosed() сообщают текущее состояние вашего сокета. Не то же самое.

  • isConnected() указывает, подключен ли этот сокет. У вас есть, поэтому он возвращает true.

  • isClosed() сообщает, закрыт ли этот сокет. Пока у вас нет, он возвращает false.

  • Если сверстник закрыл соединение упорядоченным способом

    • read() возвращает -1
    • readLine() возвращает null
    • readXXX() throws EOFException для любого другого XXX.

    • Запись будет вызывать соединение IOException: 'reset по peer', в конце концов, с задержкой буферизации.

  • Если соединение по какой-либо причине отказалось, запись будет вызывать IOException, в конце концов, как указано выше, и чтение может делать то же самое.

  • Если партнер все еще подключен, но не использует соединение, может использоваться таймаут чтения.

  • В отличие от того, что вы можете прочитать в другом месте, ClosedChannelException не говорит вам об этом. [Также не имеет значения SocketException: socket closed.]. Он сообщает вам, что вы закрыли канал, а затем продолжали его использовать. Другими словами, ошибка программирования с вашей стороны. Он не указывает на закрытое соединение.

  • В результате некоторых экспериментов с Java 7 в Windows XP также появляется, что если:

    • вы выбираете OP_READ
    • select() возвращает значение больше нуля
    • связанный SelectionKey уже недействителен (key.isValid() == false)

    это означает, что у партнера есть reset соединение. Однако это может быть характерно как для версии JRE, так и для платформы.

Ответ 2

Обща практика в различных протоколах обмена сообщениями, чтобы постоянно бить друг друга (продолжать отправлять пакеты ping), пакет не должен быть очень большим. Механизм зондирования позволит вам обнаружить отключенного клиента даже до того, как TCP определит его вообще (тайм-аут TCP намного выше). Отправьте пробник и подождите 5 секунд для ответа, если вы не видите ответ для 2-3 последующие зонды, ваш плеер отключен.

Кроме того, связанный вопрос

Ответ 3

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

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

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

Если ваш код Java не закрыл/не отключил Socket, то как еще вы могли бы получить уведомление о том, что удаленный хост закрыл ваше соединение? В конечном счете, ваш try/catch делает примерно то же самое, что и poller, слушая события в гнезде ACTUAL. Рассмотрим следующее:

  • ваша локальная система может закрыть ваш сокет, не уведомив вас... это всего лишь реализация Socket (т.е. он не проверяет аппаратное обеспечение/драйвер/прошивку/что-либо для изменения состояния).
  • новый Socket (Proxy p)... есть несколько сторон (на самом деле 6 конечных точек), которые могли бы закрыть соединение с вами...

Я думаю, что одна из особенностей абстрактных языков - это то, что вы отвлечены от мелочей. Подумайте о ключевом слове using в С# (попробуйте/наконец) для SqlConnection s или что-то еще... это просто стоимость ведения бизнеса... Я думаю, что try/catch/наконец-то является принятым и необходимым шаблоном для использования Socket.

Ответ 4

Я думаю, что это характер соединений tcp, в этих стандартах требуется около 6 минут молчания при передаче, прежде чем мы сделаем вывод, что соединение прошло! Поэтому я не думаю, что вы можете найти точное решение этой проблемы. Возможно, лучший способ - написать полезный код, чтобы угадать, когда сервер должен предположить, что пользовательское соединение закрыто.

Ответ 5

В Linux при записи() в сокет, который с другой стороны, неизвестный вам, закрыт, вызовет сигнал/исключение SIGPIPE, но вы хотите его вызвать. Однако, если вы не хотите, чтобы вас поймал SIGPIPE, вы можете использовать send() с флагом MSG_NOSIGNAL. Вызов send() будет возвращаться с -1, и в этом случае вы можете проверить errno, который скажет вам, что вы пытались написать разбитый канал (в данном случае сокет) со значением EPIPE, который согласно errno.h эквивалентен 32. В качестве реакции на EPIPE вы можете вернуться назад и попытаться снова открыть сокет и попытаться отправить свою информацию снова.

Ответ 6

Сначала я проверю, что если сокет в настоящее время равен NULL или нет, а затем, если он не закрыт

if(socket!=null && !socket.isClosed()){
//good to go
}