Вопрос-ответ

Java socket API: How to tell if a connection has been closed?

Java socket API: как определить, было ли закрыто соединение?

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

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

Есть ли какое-либо другое решение?

Переведено автоматически
Ответ 1

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


  1. isConnected() сообщает, подключили ли вы этот сокет. Подключили, поэтому возвращает true.


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


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



    • read() возвращает -1

    • readLine() ВОЗВРАТ null

    • readXXX() выдает EOFException для любого другого XXX.


    • При записи будет выдано сообщение IOException: "сброс соединения одноранговым узлом", в конечном итоге, с учетом задержек буферизации.



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


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


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


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



    • вы выбираете на OP_READ

    • select() возвращает значение больше нуля

    • связанное с ним SelectionKey уже недействительно (key.isValid() == false)


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


Ответ 2

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

Также, связанный с этим вопрос

Ответ 3

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

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


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


Если ваш Java-код не закрыл / отключил сокет, то как еще вы могли бы получить уведомление о том, что удаленный хост закрыл ваше соединение? В конечном счете, ваша попытка / перехват выполняет примерно то же самое, что делал бы опросник, прослушивающий события в РЕАЛЬНОМ сокете. Рассмотрим следующее:


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

  • новый сокет (прокси p) ... есть несколько сторон (на самом деле 6 конечных точек), которые могут закрыть соединение с вами...

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

Ответ 4

Я столкнулся с аналогичной проблемой. В моем случае клиент должен периодически отправлять данные. Надеюсь, у вас такое же требование. Затем я установил SO_TIMEOUT, socket.setSoTimeout(1000 * 60 * 5); который выбрасывается, java.net.SocketTimeoutException когда указанное время истекает. Тогда я могу легко обнаружить мертвый клиент.

java