JAVA: обработка отключения сокета

  1. Два компьютера соединены сокетным соединением. Если сервер/клиент закрывает соединение со своего конца (т.е. закрывает InputStream, OutputStream и Socket), то как я могу сообщить другому концу об отключении? Я знаю один способ - попытаться прочитать из InputStream, который выдает IOException, если соединение закрыто, но есть ли другой способ обнаружить это?
  2. Другой вопрос, я посмотрел проблему в Интернете и увидел, что inputStream.available() не решает эту проблему. Это почему?

Дополнительная информация: я прошу другой способ, потому что с моим проектом становится сложно справиться, если мне нужно попытаться прочитать из InputStrem, чтобы обнаружить отключение.


person Jisan Mahmud    schedule 03.09.2012    source источник
comment
Гм, не уверен, как еще вы ожидаете решить эту проблему. Вам все равно понадобится какое-то чтение или запись сокета, чтобы определить, что он был закрыт.   -  person Tudor    schedule 03.09.2012
comment
Думая, как вы можете сделать это, чтобы их было мгновенным уведомлением, когда другая сторона закрывает (правильно закрывает, а не просто отключает) свой сокет: 1) Поток, предназначенный для чтения сокета и помещения данных в контейнер для будущей обработки, например . Очередь блокировки массива. Когда он находит -1 или IOException, он может остановить и уведомить другие потоки о том, что обмен данными завершен (или был прерван), установив флаг, вызвав какой-либо метод или поместив маркер EOS в очередь. 2) Другие потоки могут читать очередь, когда хотят, а также проверять флаг, когда хотят.   -  person The Coordinator    schedule 08.09.2012
comment
@SaintHill Allthat ничего не добавляет к самостоятельному чтению.   -  person user207421    schedule 21.11.2014
comment
@EJP Верно. Это просто альтернативный способ сделать неизбежное :)   -  person The Coordinator    schedule 21.11.2014


Ответы (4)


пытается прочитать из InputStream, что вызывает исключение IOException

Это неправильно. Если узел закрывает сокет:

  • read() возвращает -1
  • readLine() возвращает ноль
  • readXXX() выдает EOFException для любого другого X.

Поскольку InputStream имеет только read() методов, он возвращает только -1: он не выдает IOException в EOS.

В отличие от других ответов здесь, нет метода TCP API или Socket, который сообщит вам, закрыл ли одноранговый узел соединение. Вы должны попробовать чтение или запись.

Вы должны использовать тайм-аут чтения.

InputStream.available() не решает проблему, потому что не возвращает индикацию EOS. Есть несколько правильных способов его использования, и это не одно из них.

person user207421    schedule 03.09.2012

Не существует способа O-O-O получить обратный вызов/исключение в момент разрыва соединения. Вы узнаете о разорванном соединении только тогда, когда выполните явное чтение/запись в потоке сокета.

Есть два способа чтения из сокета, а именно. Синхронно читать байт за байтом по мере их поступления; или подождите, пока желаемое количество байтов не будет доступно в потоке, а затем выполните массовое чтение. Вы выполняете проверку, вызывая available() в потоке сокета, который дает вам количество байтов, доступных в настоящее время для чтения. Во втором случае, если соединение с сокетом по какой-либо причине прервано, вы не сможете получить уведомление об этом. В этом случае вам нужно использовать механизм тайм-аута для вашего ожидания. В первом случае, когда вы выполняете явное чтение/запись, вы получаете исключение.

person Drona    schedule 03.09.2012
comment
О-О-О тут ни при чем. Нет пути вообще. Техника вызова available() не имеет ничего общего с рекомендацией. Это просто более сложный и подверженный ошибкам способ блокировки в цикле. - person user207421; 21.11.2014

Проблема не в том, "если сервер/клиент закроет соединение". Проблема в том, "что, если они не закроют соединение, а соединение все же прервется?"

Невозможно определить это без собственного протокола сердцебиения.

Другой вариант — установить для SO_KEEPALIVE значение true.

«Когда для TCP-сокета установлена ​​опция keepalive и обмен данными через сокет в любом направлении не производился в течение 2 часов (ПРИМЕЧАНИЕ: фактическое значение зависит от реализации)»

По моему опыту, это гораздо раньше, чем каждые 2 часа. Скорее ~5 минут. Если не считать использования So_KEEPALIVE, вы чертовски облажались :P

В моих коммуникационных протоколах я использую зарезервированный байт пульса, который отправляется каждые 2 секунды. Мои собственные filterInputStream и filterOutputStream отправляют/и переваривают байт сердцебиения.

person The Coordinator    schedule 03.09.2012
comment
Чувак, я посмеялся над твоим ответом :D - person Jisan Mahmud; 03.09.2012
comment
Установка SO_KEEPALIVE ничего не делает, если вы не выполняете чтение или запись, и если одноранговый узел закрывает соединение, он вообще ничего не делает. Это не тест EOS, это способ обнаружения прерванных незанятых соединений. - person user207421; 03.09.2012
comment
Нет, я понял, я смеялся над той частью, которую ты по-королевски облажался :P - person Jisan Mahmud; 03.09.2012

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

Q2 Из JavaDocs

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

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

person MadProgrammer    schedule 03.09.2012
comment
Socket.isClosed() не сообщает вам, закрыл ли партнер свой конец соединения. Он сообщает вам, закрыли ли вы этот сокет. - person user207421; 03.09.2012
comment
Клиент не будет выдавать IOException. Он может выдать EOFException, в зависимости от того, какой метод чтения он вызвал. - person user207421; 27.10.2015
comment
@EJP Поскольку EOFException расширяется от IOException, это примерно одно и то же, в зависимости от того, какой уровень исключения вы хотите обработать. Могущественная часть удивительна, чтобы знать - person MadProgrammer; 27.10.2015
comment
Это равносильно тому же результату только в том случае, если он выбрасывается, и ни один из трех методов read() не выбрасывает его. - person user207421; 19.01.2018