116 votes

API Java socket : Comment savoir si une connexion a été fermée ?

Je rencontre quelques problèmes avec l'API Java socket. J'essaie d'afficher le nombre de joueurs actuellement connectés à mon jeu. Il est facile de déterminer quand un joueur s'est connecté. Cependant, il semble inutilement difficile de déterminer quand un joueur s'est déconnecté en utilisant l'API socket.

Appel isConnected() sur un socket qui a été déconnecté à distance semble toujours renvoyer true . De même, l'appel à isClosed() sur une socket qui a été fermée à distance semble toujours renvoyer false . J'ai lu que pour déterminer si une socket a été fermée ou non, des données doivent être écrites sur le flux de sortie et une exception doit être levée. Cela semble être une façon vraiment impropre de gérer cette situation. Nous devrions constamment envoyer un message poubelle sur le réseau pour savoir si une socket a été fermée.

Existe-t-il une autre solution ?

216voto

EJP Points 113412

Il n'existe pas d'API TCP permettant de connaître l'état actuel de la connexion. isConnected() y isClosed() vous indique l'état actuel de votre prise . Ce n'est pas la même chose.

  1. isConnected() vous indique si vous ont connecté cette prise . Vous l'avez fait, il renvoie donc vrai.

  2. isClosed() vous indique si vous ont fermé cette prise. Tant que ce n'est pas le cas, il renvoie un message faux.

  3. Si le pair a fermé le connexion de manière ordonnée

    • read() renvoie à -1

    • readLine() retours null

    • readXXX() lancers EOFException pour tout autre XXX.

    • Une écriture déclenchera un IOException Le problème est le suivant : "réinitialisation de la connexion par l'homologue", éventuellement, sous réserve des délais de mise en mémoire tampon.

  4. Si la connexion a été interrompue pour une autre raison, l'écriture déclenchera un message d'erreur IOException Le cas échéant, comme indiqué ci-dessus, et une lecture peut faire la même chose.

  5. Si l'homologue est toujours connecté mais n'utilise pas la connexion, un délai de lecture peut être utilisé.

  6. Contrairement à ce que vous pouvez lire ailleurs, ClosedChannelException ne vous le dit pas. [Pas plus que SocketException: socket closed. Il vous indique seulement que vous a fermé le canal, et a continué à l'utiliser. En d'autres termes, il s'agit d'une erreur de programmation de votre part. Cela n'indique pas qu'il s'agit d'une connexion.

  7. Suite à quelques expériences avec Java 7 sur Windows XP, il apparaît également que si :

    • vous sélectionnez sur OP_READ
    • select() renvoie une valeur supérieure à zéro
    • l'associé SelectionKey est déjà invalide ( key.isValid() == false )

    cela signifie que l'homologue a réinitialisé la connexion. Cependant, cela peut être propre à la version du JRE ou à la plate-forme.

13voto

Kal Points 2324

Il est courant, dans divers protocoles de messagerie, de se donner des coups de cœur mutuels (envoyer des paquets ping). Le mécanisme de sondage vous permettra de détecter le client déconnecté avant même que TCP ne s'en aperçoive (le délai d'attente de TCP est beaucoup plus long). Envoyez un sondage et attendez une réponse pendant 5 secondes, par exemple, si vous ne voyez pas de réponse après 2 ou 3 sondages ultérieurs, votre joueur est déconnecté.

En outre, Question connexe

2voto

Scottley Points 91

Je vois l'autre réponse qui vient d'être postée, mais je pense que vous êtes interactif avec des clients qui jouent votre jeu, donc je peux vous proposer une autre approche (bien que BufferedReader soit certainement valable dans certains cas).

Si vous le souhaitez, vous pouvez déléguer la responsabilité de l'enregistrement au client. Par exemple, vous auriez une collection d'utilisateurs connectés avec un horodatage du dernier message reçu de chacun d'entre eux... si un client se déconnecte, vous forceriez un réenregistrement du client, mais cela conduit à la citation et à l'idée ci-dessous.

J'ai lu que pour déterminer si une prise a ou non un fermée, des données doivent être écrites sur le flux de sortie et une exception doit être capturée. Cela semble être une façon vraiment impropre de gérer cette situation.

Si votre code Java n'a pas fermé/déconnecté la socket, comment pourriez-vous être informé que l'hôte distant a fermé votre connexion ? En fin de compte, votre try/catch fait à peu près la même chose que ce que ferait un poller qui écouterait les événements sur la socket réelle. Considérez ce qui suit :

  • votre système local pourrait fermer votre socket sans vous en avertir... c'est juste l'implémentation de Socket (c'est-à-dire qu'il n'interroge pas le matériel/pilote/firmware/quoi que ce soit pour le changement d'état).
  • new Socket(Proxy p)... il y a plusieurs parties (6 points d'extrémité en fait) qui pourraient fermer la connexion...

Je pense que l'une des caractéristiques des langues abstraites est qu'elles permettent de s'affranchir des détails. Pensez au mot-clé using en C# (try/finally) pour les SqlConnection s ou autres... c'est juste le coût de l'activité... Je pense que try/catch/finally est le modèle accepté et nécessaire pour l'utilisation de Socket.

2voto

hurelhuyag Points 5

J'ai rencontré le même problème. Dans mon cas, le client doit envoyer des données périodiquement. J'espère que vous avez le même besoin. Ensuite, j'ai défini SO_TIMEOUT socket.setSoTimeout(1000 * 60 * 5); qui est lancé java.net.SocketTimeoutException à l'expiration du délai spécifié. Je peux ainsi détecter facilement les clients morts.

1voto

Ehsan Khodarahmi Points 1493

Je pense que c'est la nature des connexions tcp, dans les normes, il faut environ 6 minutes de silence dans la transmission avant de conclure que la connexion est rompue ! Je ne pense donc pas que vous puissiez trouver une solution exacte à ce problème. Peut-être que la meilleure solution est d'écrire un code pratique pour deviner quand le serveur doit supposer que la connexion d'un utilisateur est fermée.

Prograide.com

Prograide est une communauté de développeurs qui cherche à élargir la connaissance de la programmation au-delà de l'anglais.
Pour cela nous avons les plus grands doutes résolus en français et vous pouvez aussi poser vos propres questions ou résoudre celles des autres.

Powered by:

X