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

В качестве ответа на недавний вопрос , я удивляюсь, почему в 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
...

Ответы [ 12 ]

0 голосов
/ 27 октября 2011

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

0 голосов
/ 10 октября 2008

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

...