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

89
задан Community 23 May 2017 в 11:47
поделиться

8 ответов

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

От Википедия :

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

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

термин "Завершение" также вводит в заблуждение. Существует только объявление FIN, что означает, что "Я не собираюсь отправлять Вас, больше наполняют". Но это не означает, что нет никаких пакетов в полете, или другой больше не имеет для высказывания. При реализации обычной почты как канального уровня, или если пакет переместился различные маршруты, возможно, что получатель получает пакеты в неправильном порядке. TCP знает, как зафиксировать это для Вас.

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

18
ответ дан dty 24 November 2019 в 07:22
поделиться

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

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

11
ответ дан Mike Dimmick 24 November 2019 в 07:22
поделиться

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

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

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

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

8
ответ дан Alexander 24 November 2019 в 07:22
поделиться

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

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

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

4
ответ дан user207421 24 November 2019 в 07:22
поделиться

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

Корректный ответ в 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 */  
}  
3
ответ дан Blazor 24 November 2019 в 07:22
поделиться

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

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

2
ответ дан user207421 24 November 2019 в 07:22
поделиться

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

0
ответ дан Ray 24 November 2019 в 07:22
поделиться

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

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

4
ответ дан 24 November 2019 в 07:22
поделиться
Другие вопросы по тегам:

Похожие вопросы: