servidor - socket java ejemplo
API de socket Java: ¿cómo saber si se ha cerrado una conexión? (6)
Estoy teniendo problemas con la API de socket de Java. Estoy tratando de mostrar la cantidad de jugadores actualmente conectados a mi juego. Es fácil determinar cuándo un jugador se ha conectado. Sin embargo, parece innecesariamente difícil determinar cuándo un jugador se desconectó utilizando la API de socket.
La llamada a isConnected()
en un socket que se ha desconectado de forma remota siempre parece ser true
. De forma similar, la llamada a isClosed()
en un socket que se ha cerrado de forma remota siempre parece devolver false
. He leído que para determinar realmente si se ha cerrado o no un socket, los datos se deben escribir en el flujo de salida y se debe capturar una excepción. Esto parece una forma muy impura de manejar esta situación. Simplemente tendríamos que enviar constantemente un mensaje de basura a través de la red para saber cuándo se cerró el socket.
hay alguna otra solucion?
Creo que esta es la naturaleza de las conexiones tcp, en esos estándares se tardan unos 6 minutos de silencio en la transmisión antes de concluir que ¡la conexión se ha ido! Así que no creo que puedas encontrar una solución exacta para este problema. Quizás la mejor manera es escribir algún código útil para adivinar cuándo el servidor debería suponer que se cierra una conexión de usuario.
En Linux, al escribir () en un socket que el otro lado, desconocido para usted, cerrado provocará una señal / excepción SIGPIPE, sin embargo, usted desea llamarlo. Sin embargo, si no quiere que lo atrape el SIGPIPE, puede usar send () con el indicador MSG_NOSIGNAL. La llamada a send () devolverá con -1 y en este caso puede verificar errno, lo que le indicará que intentó escribir una tubería rota (en este caso un socket) con el valor EPIPE que de acuerdo con errno.h es equivalente a 32. Como reacción al EPIPE, puede volver atrás y tratar de volver a abrir el zócalo e intentar enviar su información nuevamente.
Es una práctica general en varios protocolos de mensajería mantener el latido del corazón entre sí (sigue enviando paquetes de ping), el paquete no necesita ser muy grande. El mecanismo de prueba le permitirá detectar el cliente desconectado incluso antes de que TCP lo descubra en general (el tiempo de espera de TCP es mucho mayor) Enviar una sonda y esperar, por ejemplo, 5 segundos para una respuesta, si no ve respuesta para decir 2-3 sondas posteriores, su reproductor está desconectado.
Además, pregunta relacionada
No hay API TCP que le dirá el estado actual de la conexión. isConnected()
e isClosed()
le isClosed()
el estado actual de su socket . No es lo mismo.
isConnected()
le dice si ha conectado este conector . Usted tiene, entonces regresa verdad.isClosed()
le informa si ha cerrado este socket. Hasta que lo tenga, devuelve falso.Si el par ha cerrado la conexión de una manera ordenada
-
read()
devuelve -1 -
readLine()
devuelvenull
readXXX()
arrojaEOFException
para cualquier otra XXX.Una escritura arrojará una
IOException
: ''restablecimiento de conexión por pares'', eventualmente, sujeto a retrasos de almacenamiento en búfer.
-
Si la conexión se ha caído por cualquier otro motivo, una escritura emitirá una
IOException
, eventualmente, como seIOException
anteriormente, y una lectura puede hacer lo mismo.Si el par aún está conectado pero no está usando la conexión, se puede usar un tiempo de espera de lectura.
Al contrario de lo que puede leer en otra parte,
ClosedChannelException
no le dice esto. [Tampoco lo haceSocketException: socket closed.
] Solo dice que cerró el canal y luego continuó usándolo. En otras palabras, un error de programación de su parte. No indica una conexión cerrada .Como resultado de algunos experimentos con Java 7 en Windows XP, también parece que si:
- estás seleccionando en
OP_READ
-
select()
devuelve un valor mayor que cero - la
SelectionKey
asociada ya no es válida (key.isValid() == false
)
significa que el par ha restablecido la conexión. Sin embargo, esto puede ser peculiar de la versión o plataforma de JRE.
- estás seleccionando en
Primero compruebo que si el socket es actualmente NULL o no y luego si no está cerrado
if(socket!=null && !socket.isClosed()){
//good to go
}
Veo la otra respuesta que acabo de publicar, pero creo que eres interactivo con los clientes que juegan tu juego, por lo que puedo plantear otro enfoque (mientras que BufferedReader es definitivamente válido en algunos casos).
Si quisiera ... podría delegar la responsabilidad de "registro" al cliente. Es decir, tendría una colección de usuarios conectados con una marca de tiempo en el último mensaje recibido de cada ... si un cliente agota el tiempo de espera, usted forzaría un nuevo registro del cliente, pero eso lleva a la cita y la idea a continuación.
He leído que para determinar realmente si un socket se ha cerrado o no, los datos se deben escribir en el flujo de salida y se debe capturar una excepción. Esto parece una forma muy impura de manejar esta situación.
Si su código de Java no cerró / desconectó el Socket, ¿de qué otro modo podría recibir una notificación de que el host remoto cerró su conexión? En última instancia, su try / catch está haciendo más o menos lo mismo que un sondeador que está escuchando eventos en el socket ACTUAL. Considera lo siguiente:
- su sistema local podría cerrar su socket sin notificarle ... esa es solo la implementación de Socket (es decir, no sondea el hardware / controlador / firmware / lo que sea para el cambio de estado).
- nuevo Socket (Proxy p) ... hay varias partes (6 puntos finales realmente) que podrían estar cerrando la conexión contigo ...
Creo que una de las características de los lenguajes abstractos es que estás abstraído de las minucias. Piense en la palabra clave using en C # (try / finally) para SqlConnection s o lo que sea ... es solo el costo de hacer negocios ... Creo que try / catch / finally es el patrón aceptado y necesario para el uso de Socket.