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

j'ai des 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 lecteur s'est déconnecté en utilisant l'API socket.

appelant isConnected() sur une socket qui a été déconnecté à distance semble toujours retourner true . De même, appeler isClosed() sur une socket qui a été fermé à distance semble toujours revenir false . J'ai lu que pour réellement déterminer si une socket a été fermée, les données doivent être écrites dans le flux de sortie et une exception doit être capturée. Cela semble comme une façon vraiment impure de gérer cette situation. Nous aurions juste constamment à spamer un message d'ordures sur le réseau pour savoir jamais quand une prise avait fermé.

y a-t-il une autre solution?

62
demandé sur Nayuki 2012-04-20 09:20:59

6 réponses

il n'y a pas D'API TCP qui vous indiquera l'état actuel de la connexion. isConnected() et isClosed() dites-vous de l'état actuel de votre socket . Pas la même chose.

  1. isConnected() vous dit si vous ont connecté cette prise . Vous avez, de sorte qu'il renvoie la valeur true.

  2. isClosed() dit si vous fermé cette prise. Jusqu'à ce que vous ayez, il retourne false.

  3. Si le par les pairs a fermé la connexion ordonnée

    • read() renvoie -1
    • readLine() renvoie null
    • readXXX() lancers EOFException pour tout autre XXX.

    • une écriture lancera un IOException : "connexion réinitialisée par peer", éventuellement, sujet à des retards de tampon.

  4. si la connexion a cessé pour toute autre raison, une écriture lancera un IOException , éventuellement, comme ci-dessus, et une lecture peut faire la même chose.

  5. si le Pair est toujours connecté mais n'utilisant 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. [Ni ne dit SocketException: socket closed. ] il vous dit seulement que vous fermé le canal, et a continué à l'utiliser. En d'autres termes, une erreur de programmation de votre part. Il n'indique pas une connexion fermée .

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

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

    cela signifie que le pair a réinitialisé la connexion. Toutefois, cela pourrait être propre à la version JRE ou à la plateforme.

134
répondu user207421 2016-05-19 00:16:14

il est de pratique courante dans divers protocoles de messagerie de garder le cœur qui bat l'autre (continuer à envoyer des paquets de ping) le paquet n'a pas besoin d'être très grand. Le mécanisme de palpage vous permettra de détecter le client déconnecté avant même que TCP ne s'en aperçoive en général (le temps D'arrêt TCP est beaucoup plus élevé). envoyez une sonde et attendez par exemple 5 secondes pour une réponse, si vous ne voyez pas de réponse pour par exemple 2-3 sondes suivantes, votre lecteur est déconnecté.

aussi, question connexe

7
répondu Kalpak Gadre 2017-05-23 12:34:41

je vois que l'autre réponse vient d'être postée, mais je pense que vous êtes interactif avec les clients qui jouent votre jeu, donc je peux poser une autre approche (alors que BufferedReader est certainement valide dans certains cas).

si vous le vouliez... vous pouvez déléguer la responsabilité de l ' "inscription" au client. I. e. vous avez une collection d'utilisateurs connectés avec un horodatage sur le dernier message reçu de chacun... si un client sort, vous forcez un réenregistrement du client, mais cela conduit à la citation et l'idée ci-dessous.

j'ai lu que pour réellement déterminer si oui ou non une socket a été fermée données doivent être écrites sur le flux de sortie et une exception doit être pris. Ça semble être une façon très malsaine de gérer ça. situation.

si votre code Java ne fermait pas/ne déconnectait pas la Socket, comment vous serait-il notifié que l'hôte distant fermait votre connexion? En fin de compte, votre try/catch fait à peu près la même chose qu'un poller qui écoute des événements sur la socket réelle ferait. Considérons ce qui suit:

  • votre système local pourrait fermer votre socket sans vous en aviser... c'est juste l'implémentation de Socket (c'est-à-dire qu'il ne pollue pas le matériel/pilote/firmware/quoi que ce soit pour le changement d'état).
  • nouveau Socket(Proxy p)... il y a plusieurs parties (6 paramètres en fait) qui pourraient être fermer la connexion sur vous...

je pense que l'une des caractéristiques des langues abstraites est que vous êtes abstraits de la minutia. Pensez au mot-clé utilisé dans C# (try/finally) pour Sqlconnections ou n'importe quoi d'autre... c'est juste le coût de faire des affaires... Je pense que try / catch / finally est le modèle accepté et nécessaire pour L'utilisation de Socket.

2
répondu Scottley 2012-04-20 05:40:27

je pense que c'est la nature des connexions tcp, en ce que les normes il faut environ 6 minutes de silence dans la transmission avant de conclure que la connexion est parti! Donc je ne pense pas que vous puissiez trouver une solution exacte à ce problème. Peut-être que la meilleure façon est d'écrire du code pratique pour deviner quand le serveur devrait supposer qu'une connexion utilisateur est fermée.

1
répondu Ehsan Khodarahmi 2012-04-20 05:55:20

sur Linux quand write()ing dans une socket que l'autre côté, inconnu de vous, fermera provoquera un signal SIGPIPE/exception cependant vous voulez l'appeler. Cependant, si vous ne voulez pas être pris par le SIGPIPE, vous pouvez utiliser send() avec le drapeau MSG_NOSIGNAL. L'appel send () retournera avec -1 et dans ce cas vous pouvez vérifier errno qui vous dira que vous avez essayé d'écrire un tuyau cassé (dans ce cas une socket) avec la valeur EPIPE qui selon errno.h équivaut à 32. Comme une réaction à L'EPIPE vous pourriez revenir en arrière et essayer de rouvrir la prise et essayer d'envoyer vos informations à nouveau.

-2
répondu MikeK 2017-05-31 13:29:02

je vérifie d'abord que si la socket est actuellement nulle ou non et puis si elle n'est pas fermée

if(socket!=null && !socket.isClosed()){
//good to go
}
-5
répondu Jiraiya 2017-12-07 17:06:40