web-dev-qa-db-fra.com

Détection TCP Déconnexion du client

Disons que je suis sous un simple serveur et que j'ai accept()ed une connexion d'un client.

Quel est le meilleur moyen de savoir quand le client s'est déconnecté? Normalement, un client est censé envoyer une commande de fermeture, mais que se passe-t-il s'il se déconnecte manuellement ou s'il perd complètement la connexion réseau? Comment le serveur peut-il détecter ou gérer cela?

64
Zxaos

select (avec le masque de lecture défini) retournera avec le descripteur signalé, mais lorsque vous utiliserez ioctl * pour vérifier le nombre d'octets en attente de lecture, il sera égal à zéro. Ceci est un signe que le socket a été déconnecté.

Il s'agit d'une excellente discussion sur les différentes méthodes permettant de vérifier que le client s'est déconnecté: Stephen Cleary, Détection de connexions semi-ouvertes (abandonnées) .

* Pour Windows, utilisez ioctlsocket.

3
sep

Dans TCP, il n'y a qu'un seul moyen de détecter une déconnexion ordonnée, à savoir obtenir zéro comme valeur de retour de read()/recv()/recvXXX() lors de la lecture.

Il existe également un seul moyen fiable de détecter une connexion interrompue: en écrivant dessus. Après un nombre suffisant d'écritures sur une connexion interrompue, TCP aura effectué suffisamment de tentatives et de délais pour savoir qu'il a été rompu et fera en sorte que write()/send()/sendXXX() renvoie -1 avec une valeur errno/WSAGetLastError() de ECONNRESET, ou, dans certains cas, "délai de connexion expiré". Notez que ce dernier est différent du «délai de connexion», qui peut survenir pendant la phase de connexion.

Vous devez également définir un délai de lecture raisonnable et abandonner les connexions qui échouent.

La réponse ici à propos de ioctl() et FIONREAD est non-sens en concurrence. Tout ce que cela fait, c'est vous dire combien d'octets sont actuellement dans le tampon de réception du socket, disponibles pour être lus sans blocage. Si un client ne vous envoie pas quelque chose pendant cinq minutes, cela ne constitue pas une déconnexion, mais FIONREAD est égal à zéro. Pas la même chose: même pas proche.

92
user207421

Pour en savoir un peu plus:

Si vous utilisez un serveur, vous devez soit utiliser TCP_KEEPALIVE pour surveiller les connexions client, soit faire quelque chose de similaire, ou connaître le protocole/les données que vous exécutez via la connexion.

Fondamentalement, si la connexion est supprimée (c’est-à-dire qu’elle n’est pas correctement fermée), le serveur ne le remarquera que s’il essaie d’écrire quelque chose sur le client. Sinon, si vous connaissez mieux le protocole, vous pouvez tout de même vous déconnecter après un délai d'inactivité.

12
Peter Jeffery

Si vous utilisez des E/S superposées (c.-à-d. Asynchrones) avec des routines d’achèvement ou des ports d’achèvement, vous serez immédiatement averti (en supposant que vous avez une lecture en attente) lorsque le côté client ferme la connexion.

2
Graeme Perrow
"""
tcp_disconnect.py
Echo network data test program in python. This easily translates to C & Java.

A server program might want to confirm that a tcp client is still connected 
before it sends a data. That is, detect if its connected without reading from socket.
This will demonstrate how to detect a TCP client disconnect without reading data.

The method to do this:
1) select on socket as poll (no wait)
2) if no recv data waiting, then client still connected
3) if recv data waiting, the read one char using PEEK flag 
4) if PEEK data len=0, then client has disconnected, otherwise its connected.
Note, the peek flag will read data without removing it from tcp queue.

To see it in action: 0) run this program on one computer 1) from another computer, 
connect via telnet port 12345, 2) type a line of data 3) wait to see it echo, 
4) type another line, 5) disconnect quickly, 6) watch the program will detect the 
disconnect and exit.

John Masinter, 17-Dec-2008
"""

import socket
import time
import select

Host = ''       # all local interfaces
PORT = 12345    # port to listen

# listen for new TCP connections
s = socket.socket(socket.AF_INET, socket.SOCK_STREAM)
s.setsockopt(socket.SOL_SOCKET, socket.SO_REUSEADDR, 1)
s.bind((Host, PORT))
s.listen(1)
# accept new conneciton
conn, addr = s.accept()
print 'Connected by', addr
# loop reading/echoing, until client disconnects
try:
    conn.send("Send me data, and I will echo it back after a short delay.\n")
    while 1:
        data = conn.recv(1024)                          # recv all data queued
        if not data: break                              # client disconnected
        time.sleep(3)                                   # simulate time consuming work
        # below will detect if client disconnects during sleep
        r, w, e = select.select([conn], [], [], 0)      # more data waiting?
        print "select: r=%s w=%s e=%s" % (r,w,e)        # debug output to command line
        if r:                                           # yes, data avail to read.
            t = conn.recv(1024, socket.MSG_PEEK)        # read without remove from queue
            print "peek: len=%d, data=%s" % (len(t),t)  # debug output
            if len(t)==0:                               # length of data peeked 0?
                print "Client disconnected."            # client disconnected
                break                                   # quit program
        conn.send("-->"+data)                           # echo only if still connected
finally:
    conn.close()
0
John Masinter

Essayez de chercher EPOLLHUP ou EPOLLERR. Comment vérifier si la connexion du client est toujours active

Lire et chercher 0 marchera dans certains cas, mais pas tous.

0
Trade-Ideas Philip

La valeur de retour de receive sera -1 si la connexion est perdue, sinon ce sera la taille du tampon.

void ReceiveStream(void *threadid)
{
    while(true)
    {
        while(ch==0)
        {
            char buffer[1024];
            int newData;
            newData = recv(thisSocket, buffer, sizeof(buffer), 0);
            if(newData>=0)
            {
                std::cout << buffer << std::endl;
            }
            else
            {
                std::cout << "Client disconnected" << std::endl;
                if (thisSocket)
                {
                    #ifdef WIN32
                        closesocket(thisSocket);
                        WSACleanup();
                    #endif
                    #ifdef LINUX
                        close(thisSocket);
                    #endif
                }
                break;
            }
        }
        ch = 1;
        StartSocket();
    }
}
0
Anand Paul

TCP a des procédures "ouvertes" et "fermées" dans le protocole. Une fois "ouvert", une connexion est maintenue jusqu'à "fermée". Mais beaucoup de choses peuvent arrêter le flux de données de manière anormale. Cela étant dit, les techniques permettant de déterminer s’il est possible d’utiliser un lien dépendent fortement des couches logicielles existant entre le protocole et le programme d’application. Ceux mentionnés ci-dessus se concentrent sur un programmeur essayant d'utiliser un socket de manière non invasive (lecture ou écriture de 0 octets) sont peut-être les plus courants. Certaines couches dans les bibliothèques fourniront le "polling" pour un programmeur. Par exemple, les appels Win32 asych (retardés) peuvent démarrer une lecture qui retournera sans erreur et 0 octet pour signaler un socket qui ne peut plus être lu (probablement une procédure FIN TCP). D'autres environnements peuvent utiliser des "événements" tels que définis dans leurs couches d'habillage. Il n'y a pas de réponse unique à cette question. Le mécanisme permettant de détecter quand un socket ne peut pas être utilisé et doit être fermé dépend des wrappers fournis dans les bibliothèques. Il est également intéressant de noter que les sockets eux-mêmes peuvent être réutilisés par des couches situées sous une bibliothèque d'applications. Il est donc sage de comprendre comment votre environnement traite l'interface Berkley Sockets.

0
jlpayton