web-dev-qa-db-fra.com

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

Je rencontre des problèmes avec l'API de socket Java. 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é à l'aide de l'API de socket.

L'appel de isConnected() sur un socket déconnecté à distance semble toujours retourner true. De même, appeler isClosed() sur un socket qui a été fermé à distance semble toujours renvoyer false. J'ai lu que pour déterminer si un socket a été fermé ou non, les données doivent être écrites dans le flux de sortie et une exception doit être interceptée. Cela semble être un moyen vraiment impur de gérer cette situation. Nous aurions simplement à envoyer constamment un message indésirable sur le réseau pour savoir quand un socket est fermé.

N 'y a-t-il pas une autre solution?

65
Dan Brouwer

Il n'y a pas d'API TCP qui vous indique l'état actuel de la connexion. isConnected() et isClosed() vous indiquent l’état actuel de votre socket . Pas la même chose.

  1. isConnected() vous indique si vous avez connecté cette prise . Vous avez, alors cela retourne vrai.

  2. isClosed() vous indique si vous avez fermé cette socket. Jusqu'à ce que vous ayez, il retourne faux.

  3. Si le pair a fermé la connexionde manière ordonnée 

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

    • Une écriture jette une IOException: 'connexion réinitialisée par un pair', éventuellement soumise à des délais de mise en mémoire tampon.

  4. Si la connexion a été interrompue pour une autre raison, une écriture jette une IOException, éventuellement comme 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 dit pas cela. [SocketException: socket closed.] non plus. Il vous dit seulement que vous avez fermé le canal, et ensuite continué à l'utiliser. En d'autres termes, une erreur de programmation de votre part. Cela 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() renvoie une valeur supérieure à zéro
    • la SelectionKey associée est déjà invalide (key.isValid() == false)

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

144
user207421

Dans divers protocoles de messagerie, il est de pratique courante de garder des battements de cœur (envoi continu de paquets ping), le paquet n’a pas besoin d’être très volumineux. Le mécanisme de sondage vous permettra de détecter le client déconnecté avant même que TCP ne le découvre en général (le délai d’expiration du protocole TCP est beaucoup plus long). dire 2-3 sondes suivantes, votre lecteur est déconnecté.

Aussi, question liée

6
Kalpak Gadre

Je vois l’autre réponse qui vient d’être publiée, mais je pense que vous êtes interactif avec les clients qui jouent à votre jeu. Il est donc possible que je pose une autre approche (alors que BufferedReader est définitivement valable dans certains cas).

Si vous vouliez ... vous pouvez déléguer la responsabilité de "l'enregistrement" au client. C'est à dire. vous auriez une collection d'utilisateurs connectés avec un horodatage sur le dernier message reçu de chacun ... si un client arrive à expiration, vous forceriez une réenregistrement du client, mais cela conduit au devis et à l'idée ci-dessous.

J'ai lu cela pour déterminer si un socket a ou non Les données été fermées doivent être écrites dans le flux de sortie et une exception doit être attrapé. Cela semble être un moyen vraiment impur de gérer cela situation.

Si votre code Java ne ferme/déconnecte pas le Socket, alors de quelle autre manière seriez-vous informé que l'hôte distant a fermé votre connexion? En fin de compte, votre essai/capture fait à peu près la même chose qu’un sondeur écoutant pour des événements sur le socket ACTUAL. Considérer ce qui suit:

  • votre système local pourrait fermer votre socket sans vous en avertir… il s’agit simplement de l’implémentation de Socket (c’est-à-dire qu’il n’interroge pas le matériel/pilote/micrologiciel/quel que soit le changement d’état).
  • new Socket (Proxy p) ... il y a plusieurs parties (6 points d'extrémité vraiment) qui pourraient fermer la connexion sur vous ...

Je pense que l’une des caractéristiques des langages abstraits est que vous êtes abstraits des minuties. Pensez au mot-clé using en C # (try/finally) pour SqlConnection s ou quoi que ce soit ... c’est juste le coût des affaires ... Je pense que try/catch/finally est le modèle accepté et nécessaire pour l’utilisation de Socket.

2
Scottley

Je pense que c’est la nature des connexions TCP, dans la mesure où les normes prennent environ 6 minutes de silence en transmission avant de conclure que la connexion a disparu! Je ne pense donc pas que vous puissiez trouver une solution exacte à ce problème. Le meilleur moyen est peut-être d'écrire du code pratique pour deviner quand le serveur doit supposer qu'une connexion utilisateur est fermée.

1
Ehsan Khodarahmi

Thats comment je le gère

 while(true) {
        if((receiveMessage = receiveRead.readLine()) != null ) {  

        System.out.println("first message same :"+receiveMessage);
        System.out.println(receiveMessage);      

        }
        else if(receiveRead.readLine()==null)
        {

        System.out.println("Client has disconected: "+sock.isClosed()); 
        System.exit(1);
         }    } 

si le result.code == null

0
Petar Ceho

Comme @ user207421 le dit, il n’existe aucun moyen de connaître l’état actuel de la connexion en raison du modèle d’architecture de protocole TCP/IP. Donc, le serveur doit vous remarquer avant de fermer la connexion ou vous le vérifiez vous-même. 
Voici un exemple simple qui montre comment savoir que le socket est fermé par le serveur:

sockAdr = new InetSocketAddress(SERVER_HOSTNAME, SERVER_PORT);
socket = new Socket();
timeout = 5000;
socket.connect(sockAdr, timeout);
reader = new BufferedReader(new InputStreamReader(socket.getInputStream());
while ((data = reader.readLine())!=null) 
      log.e(TAG, "received -> " + data);
log.e(TAG, "Socket closed !");
0
ucMedia