88 votes

Détection instantanée de la déconnexion du client du socket du serveur.

Comment puis-je détecter qu'un client s'est déconnecté de mon serveur ?

J'ai le code suivant dans mon AcceptCallBack méthode

static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
  //Accept incoming connection
  Socket listener = (Socket)ar.AsyncState;
  handler = listener.EndAccept(ar);
}

Je dois trouver un moyen de découvrir le plus rapidement possible que le client s'est déconnecté de l'ordinateur. handler Prise de courant.

J'ai essayé :

  1. handler.Available;
  2. handler.Send(new byte[1], 0, SocketFlags.None);
  3. handler.Receive(new byte[1], 0, SocketFlags.None);

Les approches ci-dessus fonctionnent lorsque vous vous connectez à un serveur et que vous souhaitez détecter le moment où le serveur se déconnecte, mais elles ne fonctionnent pas. lorsque vous êtes le serveur et que vous voulez détecter la déconnexion du client.

Toute aide sera appréciée.

126voto

Samuel Points 21085

Comme aucun événement n'est disponible pour signaler que la prise est déconnectée, vous devrez l'interroger à une fréquence acceptable pour vous.

En utilisant cette méthode d'extension, vous pouvez disposer d'une méthode fiable pour détecter si une prise est déconnectée.

static class SocketExtensions
{
  public static bool IsConnected(this Socket socket)
  {
    try
    {
      return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0);
    }
    catch (SocketException) { return false; }
  }
}

29voto

Majak Points 1313

Quelqu'un a mentionné la capacité keepAlive de TCP Socket. Ici, elle est joliment décrite :

http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html

Je l'utilise de la façon suivante : après la connexion du socket, j'appelle cette fonction, qui active keepAlive. Le site keepAliveTime spécifie le délai d'attente, en millisecondes, sans activité jusqu'à l'envoi du premier paquet keep-alive. Le site keepAliveInterval spécifie l'intervalle, en millisecondes, entre l'envoi de paquets keep-alive successifs si aucun accusé de réception n'est reçu.

    void SetKeepAlive(bool on, uint keepAliveTime, uint keepAliveInterval)
    {
        int size = Marshal.SizeOf(new uint());

        var inOptionValues = new byte[size * 3];

        BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)keepAliveTime).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)keepAliveInterval).CopyTo(inOptionValues, size * 2);

        socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null);
    }

J'utilise également la lecture asynchrone :

socket.BeginReceive(packet.dataBuffer, 0, 128,
                    SocketFlags.None, new AsyncCallback(OnDataReceived), packet);

Et dans le callback, voici le timeout capturé SocketException qui se déclenche lorsque la socket ne reçoit pas de signal ACK après le paquet keep-alive.

public void OnDataReceived(IAsyncResult asyn)
{
    try
    {
        SocketPacket theSockId = (SocketPacket)asyn.AsyncState;

        int iRx = socket.EndReceive(asyn);
    }
    catch (SocketException ex)
    {
        SocketExceptionCaught(ex);
    }
}

De cette façon, je suis en mesure de détecter en toute sécurité la déconnexion entre le client TCP et le serveur.

15voto

Erik Funkenbusch Points 53436

Ce n'est tout simplement pas possible. Il n'y a pas de connexion physique entre vous et le serveur (sauf dans le cas extrêmement rare où vous vous connectez entre deux ordinateurs avec un câble de bouclage).

Lorsque la connexion est fermée gracieusement, l'autre partie est notifiée. Mais si la connexion est déconnectée d'une autre manière (disons que la connexion de l'utilisateur est interrompue), le serveur ne le saura pas jusqu'à ce qu'il s'arrête (ou tente d'écrire sur la connexion et que l'ack s'arrête). C'est la façon dont TCP fonctionne et vous devez vous en accommoder.

Par conséquent, "instantanément" n'est pas réaliste. Le mieux que vous puissiez faire est de respecter le délai d'attente, qui dépend de la plate-forme sur laquelle le code est exécuté.

EDIT : Si vous ne recherchez que des connexions gracieuses, pourquoi ne pas simplement envoyer une commande "DISCONNECT" au serveur depuis votre client ?

6voto

ATC Points 21

"C'est comme ça que fonctionne le TCP et il faut vivre avec."

Oui, tu as raison. C'est un fait de la vie que j'ai fini par réaliser. Vous constaterez le même comportement même dans les applications professionnelles utilisant ce protocole (et même d'autres). Je l'ai même vu se produire dans des jeux en ligne ; votre ami dit "au revoir", et il semble être en ligne pendant encore 1 à 2 minutes jusqu'à ce que le serveur "fasse le ménage".

Vous pouvez utiliser les méthodes suggérées ici, ou mettre en place un "battement de cœur", comme cela est également suggéré. Je choisis la première solution. Mais si je choisissais la seconde méthode, je demanderais simplement au serveur de faire un "ping" à chaque client de temps en temps avec un seul octet, et de voir s'il y a un délai d'attente ou pas de réponse. Vous pourriez même utiliser un thread d'arrière-plan pour réaliser cela avec un timing précis. Peut-être même qu'une combinaison pourrait être implémentée dans une sorte de liste d'options (enum flags ou quelque chose comme ça) si vous êtes vraiment inquiet à ce sujet. Mais ce n'est pas si grave d'avoir un petit retard dans la mise à jour du serveur, tant que vous faites la mise à jour. C'est l'internet, et personne ne s'attend à ce qu'il soit magique ! :)

3voto

Niran Points 447

L'implémentation de Heartbeat dans votre système pourrait être une solution. Cela n'est possible que si le client et le serveur sont tous deux sous votre contrôle. Vous pouvez avoir un objet DateTime qui garde la trace de l'heure à laquelle les derniers octets ont été reçus du socket. Et supposer que le socket qui n'a pas répondu pendant un certain intervalle est perdu. Cela ne fonctionnera que si vous avez implémenté le heartbeat/custom keep alive.

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