Me encuentro con algunos problemas con la API de socket de Java. Estoy tratando de mostrar la cantidad de jugadores conectados actualmente a mi juego. Es fácil determinar cuándo se ha conectado un jugador. Sin embargo, parece innecesariamente difícil determinar cuándo un jugador se ha desconectado usando la API de socket.
Llamar isConnected()
a un enchufe que se ha desconectado de forma remota siempre parece volver true
. Del mismo modo, la llamada isClosed()
a un socket que se ha cerrado de forma remota siempre parece volver false
. He leído que para determinar realmente si un socket se ha cerrado o no, los datos deben escribirse en el flujo de salida y debe detectarse una excepción. Esta parece una forma realmente sucia de manejar esta situación. Tendríamos que enviar constantemente un mensaje basura a través de la red para saber cuándo se había cerrado un socket.
hay alguna otra solucion?
fuente
0
lugar de-1
.Es una práctica general en varios protocolos de mensajería mantener latidos entre sí (seguir enviando paquetes de ping), el paquete no necesita ser muy grande. El mecanismo de sondeo le permitirá detectar el cliente desconectado incluso antes de que TCP se dé cuenta en general (el tiempo de espera de TCP es mucho mayor) Envíe un sondeo y espere 5 segundos para recibir una respuesta, si no ve la respuesta para, digamos 2-3 sondas posteriores, su reproductor está desconectado.
Además, pregunta relacionada
fuente
Veo la otra respuesta que se acaba de publicar, pero creo que es interactivo con los clientes que juegan su 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" en el cliente. Es decir, tendría una colección de usuarios conectados con una marca de tiempo en el último mensaje recibido de cada uno ... si un cliente agota el tiempo de espera, forzaría un nuevo registro del cliente, pero eso lleva a la cita y la idea a continuación.
Si su código Java no cerró / desconectó el Socket, ¿de qué otra manera se le notificaría que el host remoto cerró su conexión? En última instancia, su try / catch está haciendo más o menos lo mismo que haría un encuestador que escucha eventos en el socket ACTUAL. Considera lo siguiente:
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 # (probar / finalmente) para SqlConnection so lo que sea ... es solo el costo de hacer negocios ... Creo que probar / atrapar / finalmente es el patrón aceptado y necesario para el uso de Socket.
fuente
Creo que esta es la naturaleza de las conexiones tcp, en esos estándares, se necesitan aproximadamente 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 un código útil para adivinar cuándo el servidor debería suponer que la conexión de un usuario está cerrada.
fuente
Como dice @ user207421, no hay forma de saber el estado actual de la conexión debido al modelo de arquitectura del protocolo TCP / IP. Por lo tanto, el servidor debe advertirlo antes de cerrar la conexión o usted mismo lo verifica.
Este es un ejemplo simple que muestra cómo saber que el servidor cierra el socket:
fuente
Me enfrenté a un problema similar. En mi caso el cliente debe enviar datos periódicamente. Espero que tengas el mismo requisito. Luego configuro SO_TIMEOUT,
socket.setSoTimeout(1000 * 60 * 5);
que se lanzajava.net.SocketTimeoutException
cuando expira el tiempo especificado. Entonces puedo detectar fácilmente al cliente muerto.fuente
Así es como lo manejo
si el código de resultado == nulo
fuente
readLine()
dos veces. Ya sabes que era nulo en elelse
bloque.En Linux, cuando se escribe () en un socket que el otro lado, desconocido para usted, cerrado provocará una señal / excepción SIGPIPE como quiera llamarlo. Sin embargo, si no desea que SIGPIPE lo atrape, puede usar send () con el indicador MSG_NOSIGNAL. La llamada send () regresará con -1 y en este caso puede verificar errno que le dirá que intentó escribir una tubería rota (en este caso un socket) con el valor EPIPE que según errno.h es equivalente a 32. Como reacción al EPIPE, podría retroceder e intentar reabrir el enchufe e intentar enviar su información nuevamente.
fuente
send()
llamada devolverá -1 solo si los datos salientes se almacenaron en búfer durante el tiempo suficiente para que expiren los temporizadores de envío. Es casi seguro que no sucederá en el primer envío después de la desconexión, debido al almacenamiento en búfer en ambos extremos y la naturaleza asincrónica desend()
debajo del capó.