Почему невозможно без попытки ввода-вывода обнаружить, что сокет TCP был корректно закрыт одноранговым узлом?

В ответ на недавний вопрос я Интересно, почему в Java невозможно без попытки чтения / записи в сокете TCP обнаружить, что сокет был корректно закрыт партнером? Кажется, это так, независимо от того, используется ли pre-NIO Socket или NIO SocketChannel.

Когда одноранговый узел корректно закрывает TCP-соединение, TCP-стеки на обеих сторонах соединения знают об этом факте. Сторона сервера (та, которая инициирует выключение) оказывается в состоянии FIN_WAIT2, тогда как сторона клиента (та, которая не отвечает явно на выключение) оказывается в состоянии CLOSE_WAIT. Почему в Socket или SocketChannel нет метода, который мог бы запрашивать стек TCP, чтобы узнать, разорвано ли базовое TCP-соединение? Дело в том, что стек TCP не предоставляет такую ​​информацию о состоянии? Или это дизайнерское решение, позволяющее избежать дорогостоящего обращения к ядру?

С помощью пользователей, которые уже опубликовали несколько ответов на этот вопрос, я думаю, что вижу, откуда может возникнуть проблема. Сторона, которая явно не закрывает соединение, оказывается в состоянии TCP CLOSE_WAIT, что означает, что соединение находится в процессе завершения и ожидает, пока сторона выполнит свою собственную CLOSE операцию. Думаю, вполне справедливо, что isConnected возвращает true, а isClosed возвращает false, но почему нет чего-то вроде isClosing?

Ниже приведены тестовые классы, в которых используются сокеты до NIO. Но идентичные результаты получаются при использовании NIO.

import java.net.ServerSocket;
import java.net.Socket;

public class MyServer {
  public static void main(String[] args) throws Exception {
    final ServerSocket ss = new ServerSocket(12345);
    final Socket cs = ss.accept();
    System.out.println("Accepted connection");
    Thread.sleep(5000);
    cs.close();
    System.out.println("Closed connection");
    ss.close();
    Thread.sleep(100000);
  }
}


import java.net.Socket;

public class MyClient {
  public static void main(String[] args) throws Exception {
    final Socket s = new Socket("localhost", 12345);
    for (int i = 0; i < 10; i++) {
      System.out.println("connected: " + s.isConnected() + 
        ", closed: " + s.isClosed());
      Thread.sleep(1000);
    }
    Thread.sleep(100000);
  }
}

Когда тестовый клиент соединяется с тестовым сервером, вывод остается неизменным даже после того, как сервер инициирует отключение соединения:

connected: true, closed: false
connected: true, closed: false
...

person Alexander    schedule 30.09.2008    source источник
comment
Я подумал, что должен упомянуть: протокол SCTP не имеет этой проблемы. У SCTP нет полузакрытого состояния, как у TCP, другими словами, одна сторона не может продолжать отправлять данные, пока другая сторона закрыла свой отправляющий сокет. Это должно упростить задачу.   -  person Tarnay Kálmán    schedule 12.04.2009
comment
У нас есть два почтовых ящика (розетки) ........................................... ...... Почтовые ящики отправляют почту друг другу, используя RoyalMail (IP), забывая о TCP ............................. .................... Все в порядке, почтовые ящики могут отправлять / получать почту друг другу (в последнее время много задержек), отправляя при этом без проблем. ............. Если один почтовый ящик столкнется с грузовиком и выйдет из строя ... как другой почтовый ящик узнает? Он должен быть уведомлен Королевской почтой, которая, в свою очередь, не будет знать, пока не попытается в следующий раз отправить / получить почту из этого неисправного почтового ящика ... ... эээ ...   -  person divinci    schedule 21.11.2009
comment
Если вы не собираетесь читать из сокета или писать в сокет, какое вам дело? А если вы собираетесь читать из сокета или писать в сокет, зачем делать дополнительную проверку? Какой вариант использования?   -  person David Schwartz    schedule 15.03.2014
comment
Socket.close - не изящное завершение.   -  person user253751    schedule 10.05.2014
comment
@immibis Это, безусловно, изящное завершение, если в приемном буфере сокета нет непрочитанных данных или если вы не испортили SO_LINGER.   -  person user207421    schedule 16.08.2016


Ответы (12)


Я часто использую сокеты, в основном с селекторами, и хотя я не являюсь экспертом по сетевой OSI, насколько я понимаю, вызов shutdownOutput() в сокете фактически отправляет что-то в сеть (FIN), что пробуждает мой селектор на другой стороне (такое же поведение в Язык C). Здесь у вас есть обнаружение: фактическое обнаружение операции чтения, которая завершится ошибкой при попытке ее выполнения.

В приведенном вами коде закрытие сокета приведет к отключению как входных, так и выходных потоков без возможности чтения данных, которые могут быть доступны, что приведет к их потере. Метод Java Socket.close() выполняет «изящное» отключение (противоположное тому, что я изначально думал) в том смысле, что данные, оставшиеся в выходном потоке, будут отправлены с последующим FIN, чтобы сигнализировать о его закрытии. FIN будет подтвержден другой стороной, как и любой обычный пакет 1.

Если вам нужно дождаться, пока другая сторона закроет свой сокет, вам нужно дождаться его FIN. А для этого вы должны обнаружить Socket.getInputStream().read() < 0, что означает, что вам не следует не закрывать сокет, поскольку он закроет свой InputStream.

Из того, что я сделал на C, а теперь и на Java, достижение такого синхронизированного закрытия должно происходить следующим образом:

  1. Вывод сокета выключения (отправляет FIN на другом конце, это последнее, что когда-либо будет отправлено этим сокетом). Вход все еще открыт, поэтому вы можете read() и обнаружить удаленный close()
  2. Прочтите сокет InputStream, пока мы не получим ответ-FIN с другого конца (поскольку он обнаружит FIN, он будет проходить через тот же изящный процесс разрыва соединения). Это важно для некоторых ОС, поскольку они фактически не закрывают сокет, пока один из его буферов все еще содержит данные. Они называются "призрачными" сокетами и используют номера дескрипторов в ОС (это может больше не быть проблемой для современных ОС).
  3. Закройте сокет (либо вызвав Socket.close(), либо закрыв его InputStream или OutputStream)

Как показано в следующем фрагменте кода Java:

public void synchronizedClose(Socket sok) {
    InputStream is = sok.getInputStream();
    sok.shutdownOutput(); // Sends the 'FIN' on the network
    while (is.read() > 0) ; // "read()" returns '-1' when the 'FIN' is reached
    sok.close(); // or is.close(); Now we can close the Socket
}

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

Как отметил @WarrenDew в своем комментарии, отбрасывание данных в программе (прикладной уровень) приводит к необратимому отключению на прикладном уровне: хотя все данные были получены на уровне TCP (цикл while), они отбрасываются.

1: Из "Фундаментальные сети в Java": см. инжир. 3.3 с.45, и весь §3.7, стр 43-48

person Matthieu    schedule 22.02.2012
comment
Java действительно выполняет изящное закрытие. Это не «брутально». - person user207421; 14.04.2013
comment
@EJP, постепенное отключение - это особый обмен, происходящий на уровне TCP, когда клиент должен сигнализировать о своем желании отключиться от сервера, который, в свою очередь, отправляет оставшиеся данные перед закрытием своей стороны. Часть оставшихся данных для отправки должна обрабатываться программой (хотя в большинстве случаев люди ничего не отправляют). Вызов socket.close() является жестоким, поскольку не учитывает эту сигнализацию клиент / сервер. Сервер будет уведомлен об отключении клиента только тогда, когда его собственный выходной буфер сокета будет заполнен (потому что никакие данные не подтверждаются другой стороной, которая была закрыта). - person Matthieu; 14.04.2013
comment
См. MSDN. Чтобы получить больше информации. - person Matthieu; 14.04.2013
comment
Java не заставляет приложения сначала вызывать Socket.close () вместо «отправки [ing] оставшихся данных», и это не мешает им сначала использовать завершение работы. Socket.close () просто делает то, что делает close (sd). В этом отношении Java ничем не отличается от самого API-интерфейса Berkeley Sockets. Действительно, во многих отношениях. - person user207421; 21.05.2013
comment
Точно, с этим я не согласен! :) - person Matthieu; 23.05.2013
comment
Тогда почему вы утверждаете обратное? Socket.close () не прерывает соединение, и ваш источник не говорит иначе. Ваш источник дает хороший способ для обоих концов достичь синхронизированного закрытия, так что оба одноранговых узла знают, что у другого есть все данные, но он не говорит, что данные будут потеряны, если вы этого не сделаете. Это. Попробуй как-нибудь. Вы можете получить сюрприз. - person user207421; 17.08.2013
comment
close() закроет потоки ввода и вывода, поэтому вы не сможете отправлять / получать что-либо после того, как вызовете его. Суть постепенного отключения - это то, что вы говорите: достижение синхронизированного закрытия: у другой стороны есть все данные и вы перестаете отправлять что-либо еще. Если вы close() этого не сделаете, вы закрываете сокет InputStream, который подключен к внутреннему буферу сетевого интерфейса, который может содержать данные, которые еще не были обработаны. Вот и потеря данных. ОС может позволить сокету открываться изнутри (призрачный сокет). Хотя для современных ОС это не имеет большого значения ... - person Matthieu; 17.08.2013
comment
Я должен добавить, что предпочитаю вашу терминологию синхронизированного закрытия, а не изящного отключения. В нем подробнее рассказывается об алгоритме разрыва сетевого подключения. - person Matthieu; 17.08.2013
comment
@EJP (извините, я забыл упомянуть ваше имя в своих предыдущих комментариях), похоже, что 'close ()' выполняет как 'shutdownOutput ()' / 'shutdownInput ()'. Теперь я понимаю вашу озабоченность, так как FIN действительно будет отправлен. Для меня изящный означает то, что вы назвали синхронизированным: оба сокета подтвердили свое отключение и имеют пустые буферы ввода и вывода. - person Matthieu; 19.09.2013
comment
Нет. «Изящное закрытие» означает, что все данные доставлены, за которым следует , за которым следует знак EOS. У SPX, например, не было изящного закрытия: любые данные в полете могли быть просто потеряны; и он был на 100% бесполезен из-за этого и других недостатков. То же самое происходит, когда TCP-соединение сбрасывается, а не закрывается. Эти термины уже имеют значения. Пожалуйста, не изобретайте их заново. И, пожалуйста, удалите из своего ответа дезинформацию о «жестоком закрытии». - person user207421; 19.09.2013
comment
@EJP Я редактировал, скажите, звучит ли лучше. Но что тогда происходит, когда одна сторона вызывает «закрыть» в своем сокете, в то время как данные все еще ожидают чтения в своей очереди? Для меня это не изящное завершение, хотя данные были доставлены (на удаленное оборудование), но все равно будут потеряны. - person Matthieu; 20.09.2013
comment
@Matthieu Если ваше приложение не считывает все доступные данные, это может быть некорректно на прикладном уровне, но на транспортном уровне TCP данные все равно принимаются, и соединение завершается корректно. То же самое было бы, если бы ваше приложение считывало все данные из входного потока и просто отбрасывало их. - person Warren Dew; 18.05.2014
comment
@WarrenDew именно так. Возможно, это были те моменты, которые EJP поднял в своих комментариях: для меня изящность - это уровень сети (TCP), а не приложения. То, что происходит в приложении, зависит (должно быть) только от программы. Я отредактирую свой ответ, чтобы добавить эту информацию. - person Matthieu; 19.05.2014
comment
@WarrenDew Это неверно. Если приложение закрывает сокет, пока в приемном буфере сокета все еще есть данные, соединение будет сброшено, а не закрыто с помощью FIN. - person user207421; 20.04.2016
comment
@EJP, даже если вызывается shutdownOutput()? - person Matthieu; 22.04.2016
comment
@Mattheiu Если приложение вызывает shutdownOutout(), а затем close(), когда в приемном буфере сокета есть непрочитанные данные, будет отправлено FIN, за которым следует RST. Утверждение, что Java не выполняет корректное отключение в вашем ответе, остается неверным. - person user207421; 14.09.2016
comment
@EJP спасибо за эту дополнительную информацию, она начинает обретать смысл (4 года спустя ...). Похоже, что мои предположения технически неполны, а мои формулировки неточны, поэтому я, вероятно, позже задам отдельный вопрос, чтобы уточнить детали. Спасибо, что не перестали вдалбливать и то, и другое в мою (твердую) голову! - person Matthieu; 14.09.2016
comment
Ваш ответ остается бессмысленным, начиная со второго абзаца. Пожалуйста, исправьте. - person user207421; 15.12.2016
comment
@EJP, я согласен и попытался исправить. Я хотел бы прочитать ваши комментарии по этому поводу. - person Matthieu; 16.12.2016
comment
@LeonidUsov Это просто неверно. Java read() возвращает -1 в конце потока и продолжает делать это, сколько бы раз вы это ни вызывали. C read() или recv() возвращает ноль в конце потока и продолжает делать это, сколько бы раз вы это ни вызывали. - person user207421; 13.06.2017
comment
@Matthieu «Без возможности чтения данных, которые могут быть доступны» остается неверным. Как я впервые заявил более четырех лет назад, а также в моей книге, никакие данные не отбрасываются изящным закрытием. И все, что прочитано read() после выключения, в основном представляет собой ошибку протокола приложения, и о ней следует сообщать и отлаживать, а не игнорировать. - person user207421; 13.06.2017
comment
@EJP тесты, которые я провел, показывают, если вы close() или shutdownInput() сокет, вызов read() выдает SocketException указание Socket is closed или Socket input is shutdown, даже если в его входном буфере есть данные is. Я не знаю, как получить доступ к этим данным после закрытия или выключения сокета ... - person Matthieu; 15.06.2017

Я думаю, это скорее вопрос программирования сокетов. Java просто следует традиции программирования сокетов.

Из Википедии:

TCP обеспечивает надежную и упорядоченную доставку потока байтов от одной программы на одном компьютере к другой программе на другом компьютере.

После того, как рукопожатие выполнено, TCP не делает различий между двумя конечными точками (клиентом и сервером). Термины «клиент» и «сервер» используются в основном для удобства. Таким образом, «сервер» может отправлять данные, а «клиент» может отправлять некоторые другие данные одновременно друг другу.

Термин «Закрыть» также вводит в заблуждение. Есть только декларация FIN, что означает: «Я больше не буду вам посылать». Но это не означает, что в полете нет пакетов или другому нечего больше сказать. Если вы реализуете обычную почту в качестве уровня канала передачи данных или если ваш пакет путешествовал по разным маршрутам, возможно, получатель получает пакеты в неправильном порядке. TCP знает, как это исправить.

Также у вас, как у программы, может не быть времени постоянно проверять, что находится в буфере. Итак, когда вам будет удобно, вы можете проверить, что находится в буфере. В целом текущая реализация сокета не так уж и плоха. Если бы на самом деле был isPeerClosed (), этот дополнительный вызов вам нужно было бы делать каждый раз, когда вы хотите вызвать read.

person Eugene Yokota    schedule 01.10.2008
comment
Я так не думаю, вы можете тестировать состояния в коде C как в Windows, так и в Linux !!! Java по какой-то причине может не раскрывать некоторые вещи, например, было бы неплохо раскрыть функции getsockopt, которые есть в Windows и Linux. Фактически, ответы ниже содержат некоторый код Linux C для стороны Linux. - person Dean Hiller; 24.08.2012
comment
Я не думаю, что наличие метода isPeerClosed () каким-то образом заставляет вас вызывать его перед каждой попыткой чтения. Вы можете просто вызвать его только тогда, когда вам это явно нужно. Я согласен с тем, что текущая реализация сокета не так уж плоха, даже если она требует от вас записи в выходной поток, если вы хотите узнать, закрыта ли удаленная часть сокета или нет. Потому что, если это не так, вам придется обрабатывать свои записанные данные и с другой стороны, и это такое же большое удовольствие, как сидение на вертикально ориентированном гвозде;) - person Jan Hruby; 15.01.2013
comment
Это действительно означает "больше нет пакетов в пути". FIN получен после любых данных в полете. Однако это не означает, что партнер закрыл сокет для ввода. Вы должны ** отправить что-то * и получить «сброс соединения», чтобы обнаружить это. FIN мог просто означать отключение для вывода. - person user207421; 04.06.2014

Базовый API сокетов не имеет такого уведомления.

Отправляющий TCP-стек в любом случае не будет отправлять бит FIN до последнего пакета, поэтому может быть много данных, буферизованных с момента, когда отправляющее приложение логически закрыло свой сокет, прежде чем эти данные даже будут отправлены. Точно так же данные, которые буферизуются, потому что сеть работает быстрее, чем принимающее приложение (я не знаю, возможно, вы передаете их через более медленное соединение), могут быть важны для получателя, и вы не хотите, чтобы принимающее приложение их отбрасывало. просто потому, что стек получил бит FIN.

person Mike Dimmick    schedule 30.09.2008
comment
В моем тестовом примере (возможно, я должен указать здесь ...) нет данных, отправленных / полученных через соединение специально. Итак, я почти уверен, что стек получает FIN (изящный) или RST (в некоторых не изящных сценариях). Это также подтверждает netstat. - person Alexander; 01.10.2008
comment
Конечно - если ничего не буферизовано, тогда FIN будет отправлен немедленно, в противном случае пакет будет пустым (без полезной нагрузки). Однако после FIN этот конец соединения больше не отправляет пакеты данных (он по-прежнему будет подтверждать все, что ему отправлено). - person Mike Dimmick; 01.10.2008
comment
Что происходит, так это то, что стороны соединения оказываются в ‹code› CLOSE_WAIT ‹/code› и ‹code› FIN_WAIT_2 ‹/code›, и именно в этом состоянии ‹code› isConcected () ‹/code› и ‹code› isClosed () ‹/code› по-прежнему не видит, что соединение было разорвано. - person Alexander; 01.10.2008
comment
Спасибо за ваши предложения! Думаю, теперь я лучше понимаю проблему. Я уточнил вопрос (см. Третий абзац): почему нет Socket.isClosing для проверки полузакрытых соединений? - person Alexander; 01.10.2008

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

Когда TCP-соединение установлено и один партнер вызывает close() или shutdownOutput() на своем сокете, сокет на другой стороне соединения переходит в состояние CLOSE_WAIT. В принципе, можно узнать из стека TCP, находится ли сокет в состоянии CLOSE_WAIT, не вызывая read/recv (например, getsockopt() в Linux: http://www.developerweb.net/forum/showthread.php?t=4395), но это не переносимо.

Кажется, что класс Java Socket разработан для обеспечения абстракции, сравнимой с сокетом BSD TCP, вероятно, потому, что это тот уровень абстракции, к которому люди привыкли при программировании приложений TCP / IP. Сокеты BSD - это обобщение, поддерживающее сокеты, отличные от INET (например, TCP), поэтому они не обеспечивают переносимый способ определения состояния TCP сокета.

Нет такого метода, как isCloseWait(), потому что люди, привыкшие программировать приложения TCP на уровне абстракции, предлагаемой сокетами BSD, не ожидают, что Java предоставит какие-либо дополнительные методы.

person Alexander    schedule 01.10.2008
comment
Также не может предоставлять какие-либо дополнительные методы переносимо. Может быть, они могли бы создать метод isCloseWait (), который возвращал бы false, если бы платформа его не поддерживала, но сколько программистов пострадали бы от этого, если бы они тестировали только на поддерживаемых платформах? - person ephemient; 10.10.2008
comment
похоже, это может быть переносимым для меня ... в Windows есть это msdn.microsoft.com/en-us/library/windows/desktop/ и linux это pubs.opengroup.org/onlinepubs/009695399/functions/ - person Dean Hiller; 24.08.2012
comment
Не то чтобы программисты к этому привыкли; Дело в том, что интерфейс сокета - это то, что полезно программистам. Имейте в виду, что абстракция сокета используется не только для протокола TCP. - person Warren Dew; 18.05.2014
comment
В Java нет такого метода, как isCloseWait(), потому что он поддерживается не на всех платформах. - person user207421; 04.06.2014
comment
Протокол идентификатора (RFC 1413) позволяет серверу либо оставить соединение открытым после отправки ответа, либо закрыть его без отправки дополнительных данных. Клиент идентификации Java может решить оставить соединение открытым, чтобы избежать трехстороннего рукопожатия при следующем поиске, но как он узнает, что соединение все еще открыто? Стоит ли просто попробовать, отреагировать на любую ошибку, повторно открыв соединение? Или это ошибка дизайна протокола? - person david; 22.05.2015
comment
@david Он знает, потому что другая отправка завершилась успешно, а другой прием не возвращает преждевременный конец потока. - person user207421; 13.06.2017

Определить, закрылась ли удаленная сторона сокета (TCP), можно с помощью метода java.net.Socket.sendUrgentData (int) и перехватить исключение IOException, которое он генерирует, если удаленная сторона не работает. Это было проверено между Java-Java и Java-C.

Это позволяет избежать проблемы разработки протокола связи для использования какого-либо механизма проверки связи. При отключении OOBInline на сокете (setOOBInline (false) все полученные данные OOB автоматически отбрасываются, но данные OOB все еще могут быть отправлены. Если удаленная сторона закрыта, выполняется попытка сброса соединения, которая завершается неудачно и вызывает некоторое исключение IOException .

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

person Uncle Per    schedule 10.08.2011

стек ввода-вывода Java определенно отправляет FIN, когда он разрушается при внезапном разрыве. Нет никакого смысла в том, что вы не можете это обнаружить, потому что большинство клиентов отправляют FIN только в том случае, если они закрывают соединение.

... еще одна причина, по которой я действительно начинаю ненавидеть Java-классы NIO. Вроде все ползунки.

person Community    schedule 06.07.2009
comment
Кроме того, похоже, что я получаю и конец потока только при чтении (возврат -1), когда присутствует FIN. Так что это единственный способ, которым я могу обнаружить отключение на стороне чтения. - person ; 06.07.2009
comment
Вы можете это обнаружить. Вы получаете EOS при чтении. И Java не отправляет FIN. TCP делает это. Java не реализует TCP / IP, он просто использует реализацию платформы. - person user207421; 17.08.2013

Это интересная тема. Я только что покопался в java-коде, чтобы проверить. По моему мнению, есть две отдельные проблемы: первая - это сам TCP RFC, который позволяет удаленно закрытому сокету передавать данные в полудуплексном режиме, поэтому удаленно закрытый сокет все еще остается полуоткрытым. Согласно RFC, RST не закрывает соединение, вам нужно отправить явную команду ABORT; поэтому Java позволяет отправлять данные через полузакрытый сокет

(Существует два метода чтения состояния закрытия на обеих конечных точках.)

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

person Lorenzo Boccaccia    schedule 30.09.2008
comment
Полагаю, вы говорите о RFC 793 (faqs.org/rfcs/rfc793.html) Раздел 3.5 Закрытие соединения. Я не уверен, что это объясняет проблему, потому что обе стороны завершают плавное отключение соединения и попадают в состояния, в которых они не должны отправлять / получать какие-либо данные. - person Alexander; 01.10.2008
comment
Зависит от. сколько FIN вы видите на розетке? Кроме того, это может быть проблема, специфичная для платформы: возможно, Windows отвечает на каждый FIN с помощью FIN, и соединение закрывается на обоих концах, но другие операционные системы могут не вести себя таким образом, и именно здесь возникает проблема 2 - person Lorenzo Boccaccia; 01.10.2008
comment
Я вижу только один FIN, отправленный серверной стороной (тот, который инициирует завершение работы). Я обновил вопрос о состояниях TCP. Поведение похоже на Windows XP и Linux 2.6. - person Alexander; 01.10.2008
comment
Спасибо за ваши предложения! Думаю, теперь я лучше понимаю проблему. Я уточнил вопрос (см. Третий абзац): почему нет Socket.isClosing для проверки полузакрытых соединений? - person Alexander; 01.10.2008
comment
потому что есть isOutputShutdown () и isInputShutdown (); однако не знаю, работают ли они полностью. (как обычно, они зависят от фактической реализации стандарта внутри ОС и внутри JVM) - person Lorenzo Boccaccia; 01.10.2008
comment
Нет, к сожалению, это не так. isOutputShutdown и isInputShutdown - это первое, что все пытаются сделать, столкнувшись с этим обнаружением, но оба метода возвращают false. Я только что протестировал его на Windows XP и Linux 2.6. Возвращаемые значения всех 4 методов остаются неизменными даже после попытки чтения. - person Alexander; 01.10.2008
comment
вопрос (эта проблема укусила меня некоторое время назад, и я обычно обхожу ее, но теперь, когда мы там ...): пробовали ли вы другие jvm (kaffe, gcj и т. д.)? - person Lorenzo Boccaccia; 01.10.2008
comment
Проведите быстрый тест: та же история в Blackdown 1.4.1 на Linux 2.6 - person Alexander; 01.10.2008
comment
В Java необходимо реализовать msdn.microsoft .com / en-us / library / windows / desktop / и pubs.opengroup.org/onlinepubs/009695399/functions/, чтобы мы все были счастливы, чтобы мы могли узнать состояние сокета на нашей стороне. - person Dean Hiller; 24.08.2012
comment
@LorenzoBoccaccia Методы выключения полностью реализованы. - person user207421; 14.04.2013
comment
Очень запутанный ответ. RFC не позволяет удаленно закрытому сокету продолжать отправлять данные. Если вы получаете RST, это означает, что такого соединения на одноранговом узле нет, и он прерывает соединение на вашем конце. RFC не говорит ничего другого. - person user207421; 14.04.2013
comment
Для справки, это не полудуплекс. Полудуплекс означает, что только одна сторона может отправлять одновременно; обе стороны все еще могут отправить. - person rbp; 19.05.2013
comment
isInputShutdown и isOutputShutdown проверяют локальный конец соединения - это тесты, чтобы выяснить, вызвали ли вы shutdownInput или shutdownOutput для этого Socket. Они ничего не говорят вам об удаленном конце соединения. - person Mike Dimmick; 22.08.2013

Это недостаток классов OO-сокетов Java (и всех других, на которые я смотрел) - нет доступа к системному вызову select.

Правильный ответ на C:

struct timeval tp;  
fd_set in;  
fd_set out;  
fd_set err;  

FD_ZERO (in);  
FD_ZERO (out);  
FD_ZERO (err);  

FD_SET(socket_handle, err);  

tp.tv_sec = 0; /* or however long you want to wait */  
tp.tv_usec = 0;  
select(socket_handle + 1, in, out, err, &tp);  

if (FD_ISSET(socket_handle, err) {  
   /* handle closed socket */  
}  
person Joshua    schedule 24.01.2009
comment
Вы можете сделать то же самое с getsocketop(... SOL_SOCKET, SO_ERROR, ...). - person David Schwartz; 21.10.2011
comment
АГА. и это, кажется, существует в Windows И на Linux обоих !!!! pubs.opengroup.org/onlinepubs/009695399/functions/ и msdn.microsoft.com/en- us / library / windows / desktop / ... во всем виновата java :( - person Dean Hiller; 24.08.2012
comment
Установленный дескриптор файла ошибки не указывает на закрытое соединение. Прочтите, пожалуйста, выбранное руководство: 'exceptfds - этот набор используется в исключительных условиях. На практике распространено только одно такое исключительное условие: доступность внеполосных (OOB) данных для чтения из сокета TCP ». FIN не является данными OOB. - person Jan Wrobel; 10.09.2012
comment
Ага оказалось изящно закрыто, так как читать умеет не кроме. Я всегда писал свой код так, как будто он тоже, попробуй прочитать (и мы знаем, что он не будет блокироваться), и я никогда не использовал OOB. - person Joshua; 11.09.2012
comment
На самом деле это не обнаружило закрытый сокет для меня (с использованием QNX). Я обнаружил, что этот stackoverflow.com/a/5640189/1326370 работал у меня. - person sclarke81; 15.07.2013
comment
Топологически эквивалентны. - person Joshua; 15.07.2013
comment
Вы можете использовать класс Selector для доступа к системному вызову select (). Однако он использует NIO. - person Matthieu; 19.09.2013
comment
Нет ничего необычного в соединении, которое было отключено другой стороной. - person David Schwartz; 15.03.2014
comment
Многие платформы, включая Java, do предоставляют доступ к системному вызову select (). - person user207421; 04.06.2014

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

person Dean Hiller    schedule 24.08.2012
comment
Как получить уведомление о закрытии сокета при использовании SSL в java? - person Dave B; 17.07.2014

Причина такого поведения (которое не является специфическим для Java) заключается в том, что вы не получаете никакой информации о состоянии из стека TCP. В конце концов, сокет - это просто еще один дескриптор файла, и вы не можете узнать, есть ли реальные данные для чтения из него, не пытаясь на самом деле (select(2) здесь не поможет, он только сигнализирует о том, что вы можете попробовать без блокировки).

Для получения дополнительной информации см. FAQ по сокетам Unix.

person WMR    schedule 01.10.2008
comment
Сокеты REALbasic (в Mac OS X и Linux) основаны на сокетах BSD, но RB умудряется выдать вам приятную ошибку 102, когда соединение разрывается на другом конце. Итак, я согласен с исходным плакатом, это должно быть возможно, и ужасно, что Java (и Какао) не предоставляют этого. - person Joe Strout; 13.05.2010
comment
@JoeStrout RB может делать это только тогда, когда вы выполняете ввод-вывод. Не существует API, который сообщал бы вам о состоянии соединения без выполнения операций ввода-вывода. Период. Это не недостаток Java. Фактически это связано с отсутствием «гудка» в TCP, что является преднамеренной особенностью дизайна. - person user207421; 17.08.2013
comment
select() сообщает вам, есть ли данные или EOS, доступные для чтения без блокировки. «Сигналы, которые можно попробовать без блокировки» бессмысленны. Если вы находитесь в неблокирующем режиме, вы можете всегда попробовать без блокировки. select() управляется данными в буфере приема сокета или ожидающим FIN или пространством в буфере отправки сокета. - person user207421; 26.02.2016
comment
@EJP А что насчет getsockopt(SO_ERROR)? Фактически, даже getpeername сообщит вам, подключен ли сокет. - person David Schwartz; 14.09.2016

Только записи требуют обмена пакетами, что позволяет определить потерю соединения. Обычный обходной путь - использовать опцию KEEP ALIVE.

person Ray    schedule 10.10.2008
comment
Я думаю, что конечной точке разрешено инициировать плавное завершение соединения, отправив пакет с установленным FIN, без записи какой-либо полезной нагрузки. - person Alexander; 11.10.2008
comment
@Alexander: Конечно, есть, но это не имеет отношения к этому ответу, который касается обнаружения меньшего количества соединений. - person user207421; 16.08.2016

Когда дело доходит до полуоткрытых сокетов Java, можно посмотреть isInputShutdown () и isOutputShutdown ().

person JimmyB    schedule 27.10.2011
comment
Нет. Это говорит вам только то, что вы звонили, а не то, что звонил друг. - person user207421; 14.04.2013
comment
Не хотите поделиться своим источником этого заявления? - person JimmyB; 19.04.2013
comment
Хотите поделиться своим источником для противоположного? Это ваше заявление. Если у вас есть доказательство, давайте его. Я утверждаю, что вы неправы. Проведите эксперимент и докажите, что я ошибаюсь. - person user207421; 17.08.2013
comment
Три года спустя и никаких экспериментов. QED - person user207421; 16.08.2016