W odpowiedzi na niedawne pytanie zastanawiam się, dlaczego w Javie nie jest możliwe wykrycie, że gniazdo zostało bezpiecznie zamknięte przez równorzędnego, bez próby odczytu / zapisu w gnieździe TCP? Wydaje się, że tak jest niezależnie od tego, czy używa się pre-NIO, Socket
czy NIO SocketChannel
.
Kiedy peer z wdziękiem zamyka połączenie TCP, stos TCP po obu stronach połączenia wie o tym fakcie. Strona serwera (ta, która inicjuje zamknięcie) kończy się w stanie FIN_WAIT2
, podczas gdy strona klienta (ta, która nie reaguje jawnie na zamknięcie) kończy się w stanie CLOSE_WAIT
. Dlaczego nie jest to metoda w Socket
lub SocketChannel
że może zapytać stos TCP, aby zobaczyć, czy połączenie TCP zostało zakończone? Czy to dlatego, że stos TCP nie dostarcza takich informacji o stanie? A może jest to decyzja projektowa, aby uniknąć kosztownego wywołania jądra?
Z pomocą użytkowników, którzy już opublikowali kilka odpowiedzi na to pytanie, myślę, że wiem, skąd może pochodzić problem. Strona, która nie zamyka jawnie połączenia, przechodzi w stan TCP, CLOSE_WAIT
co oznacza, że połączenie jest w trakcie zamykania i czeka, aż strona wykona własną CLOSE
operację. Przypuszczam, że to sprawiedliwe, że isConnected
wraca true
i isClosed
wraca false
, ale dlaczego nie ma czegoś takiego isClosing
?
Poniżej znajdują się klasy testowe, które używają gniazd pre-NIO. Ale identyczne wyniki uzyskuje się przy użyciu 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);
}
}
Gdy klient testowy łączy się z serwerem testowym, dane wyjściowe pozostają niezmienione nawet po zainicjowaniu przez serwer zamykania połączenia:
connected: true, closed: false
connected: true, closed: false
...