Como continuación de una pregunta reciente , me pregunto por qué es imposible en Java, sin intentar leer / escribir en un socket TCP, detectar que el par ha cerrado el socket con gracia. Este parece ser el caso independientemente de si se usa el pre-NIO Socket
o el NIO SocketChannel
.
Cuando un par cierra con gracia una conexión TCP, las pilas de TCP en ambos lados de la conexión conocen el hecho. El lado del servidor (el que inicia el apagado) termina en estado FIN_WAIT2
, mientras que el lado del cliente (el que no responde explícitamente al apagado) termina en estado CLOSE_WAIT
. ¿Por qué no hay un método en Socket
o SocketChannel
que pueda consultar la pila TCP para ver si la conexión TCP subyacente se ha terminado? ¿Es que la pila TCP no proporciona tal información de estado? ¿O es una decisión de diseño para evitar una llamada costosa al kernel?
Con la ayuda de los usuarios que ya han publicado algunas respuestas a esta pregunta, creo que veo de dónde podría venir el problema. El lado que no cierra explícitamente la conexión termina en el estado TCP, CLOSE_WAIT
lo que significa que la conexión está en proceso de cerrarse y espera a que el lado emita su propia CLOSE
operación. Supongo que es bastante justo que isConnected
regrese true
y isClosed
regrese false
, pero ¿por qué no hay algo así isClosing
?
A continuación se muestran las clases de prueba que utilizan sockets pre-NIO. Pero se obtienen resultados idénticos utilizando 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);
}
}
Cuando el cliente de prueba se conecta al servidor de prueba, la salida permanece sin cambios incluso después de que el servidor inicia el cierre de la conexión:
connected: true, closed: false
connected: true, closed: false
...