web-dev-qa-db-fra.com

Comment vérifier si une prise est connectée/déconnectée en C #?

Comment vérifier si un socket réseau (System.Net.Sockets.Socket) est toujours connecté si l'autre hôte ne vous envoie pas de paquet lorsqu'il se déconnecte (par exemple, parce qu'il s'est déconnecté de manière incongrue)?

56
lesderid

En tant que Paul Turner answer Socket.Connected ne peut pas être utilisé dans cette situation. Vous devez interroger la connexion à chaque fois pour voir si la connexion est toujours active. C'est le code que j'ai utilisé:

bool SocketConnected(Socket s)
{
    bool part1 = s.Poll(1000, SelectMode.SelectRead);
    bool part2 = (s.Available == 0);
    if (part1 && part2)
        return false;
    else
        return true;
}

Cela fonctionne comme ceci:

  • s.Poll renvoie true si
    • la connexion est fermée, réinitialisée, terminée ou en attente (c'est-à-dire aucune connexion active)
    • la connexion est active et des données sont disponibles pour la lecture
  • s.Available renvoie le nombre d'octets disponibles pour la lecture.
  • si les deux sont vrais:
    • il n'y a pas de données disponibles à lire, donc la connexion n'est pas active
87
zendar

Comme zendar a écrit, il est agréable d’utiliser les Socket.Poll et Socket.Available, mais vous devez prendre en compte le fait que le socket n’a peut-être pas été initialisé. C'est la dernière information (je crois) qui est fournie par la propriété Socket.Connected. La version révisée de la méthode ressemblerait à ceci:

 static bool IsSocketConnected(Socket s)
    {
        return !((s.Poll(1000, SelectMode.SelectRead) && (s.Available == 0)) || !s.Connected);

/* The long, but simpler-to-understand version:

        bool part1 = s.Poll(1000, SelectMode.SelectRead);
        bool part2 = (s.Available == 0);
        if ((part1 && part2 ) || !s.Connected)
            return false;
        else
            return true;

*/
    }
23
Automatico

La propriété Socket.Connected vous dira si un socket pense qu’il est connecté. Il reflète en fait l'état de la dernière opération d'envoi/réception effectuée sur le socket.

Si le socket a été fermé par vos propres actions (en disposer, appeler des méthodes pour se déconnecter), Socket.Connected renverra false. Si le socket a été déconnecté par un autre moyen, la propriété retournera true jusqu'à la prochaine tentative d'envoi ou de réception d'informations. À ce stade, une SocketException ou ObjectDisposedException sera renvoyée.

Vous pouvez vérifier la propriété après que l'exception se soit produite, mais elle n'est pas fiable auparavant.

13
Paul Turner

La réponse acceptée ne semble pas fonctionner si vous débranchez le câble réseau. Ou le serveur tombe en panne. Ou votre routeur tombe en panne. Ou si vous oubliez de payer votre facture internet. Définissez les options de maintien en activité TCP pour une meilleure fiabilité.

public static class SocketExtensions
{
    public static void SetSocketKeepAliveValues(this Socket instance, int KeepAliveTime, int KeepAliveInterval)
    {
        //KeepAliveTime: default value is 2hr
        //KeepAliveInterval: default value is 1s and Detect 5 times

        //the native structure
        //struct tcp_keepalive {
        //ULONG onoff;
        //ULONG keepalivetime;
        //ULONG keepaliveinterval;
        //};

        int size = Marshal.SizeOf(new uint());
        byte[] inOptionValues = new byte[size * 3]; // 4 * 3 = 12
        bool OnOff = true;

        BitConverter.GetBytes((uint)(OnOff ? 1 : 0)).CopyTo(inOptionValues, 0);
        BitConverter.GetBytes((uint)KeepAliveTime).CopyTo(inOptionValues, size);
        BitConverter.GetBytes((uint)KeepAliveInterval).CopyTo(inOptionValues, size * 2);

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



// ...
Socket sock;
sock.SetSocketKeepAliveValues(2000, 1000);

La valeur time définit le délai d'attente depuis la dernière transmission des données. Il tente ensuite d'envoyer et de recevoir un paquet persistant. En cas d'échec, il tente 10 fois (nombre codé en dur depuis Vista AFAIK) dans l'intervalle spécifié avant de décider que la connexion est morte.

Les valeurs ci-dessus entraîneraient donc une détection de 2 + 10 * 1 = 12 secondes. Après cela, toute opération de lecture/écriture/interrogation devrait échouer sur le socket.

4
toster-cx

J'ai créé une méthode d'extension basée sur this MSDN article ..__ C'est comment vous pouvez déterminer si un socket est toujours connecté.

public static bool IsConnected(this Socket client)
{
    bool blockingState = client.Blocking;

    try
    {
        byte[] tmp = new byte[1];

        client.Blocking = false;
        client.Send(tmp, 0, 0);
        return true;
    }
    catch (SocketException e)
    {
        // 10035 == WSAEWOULDBLOCK
        if (e.NativeErrorCode.Equals(10035))
        {
            return true;
        }
        else
        {
            return false;
        }
    }
    finally
    {
        client.Blocking = blockingState;
    }
}
3
Kim Ki Won

Suivant les conseils de NibblyPig et zendar , j’ai trouvé le code ci-dessous, qui fonctionne pour tous les tests que j’ai faits. J'ai fini par avoir besoin du ping et du sondage. Le ping me permet de savoir si le câble a été déconnecté ou si la couche physique a été perturbée (routeur mis hors tension, etc.). Mais parfois, après avoir reconnecté, je reçois un RST, le ping est ok, mais pas l’état TCP.

#region CHECKS THE SOCKET'S HEALTH
    if (_tcpClient.Client.Connected)
    {
            //Do a ping test to see if the server is reachable
            try
            {
                Ping pingTest = new Ping()
                PingReply reply = pingTest.Send(ServeripAddress);
                if (reply.Status != IPStatus.Success) ConnectionState = false;
            } catch (PingException) { ConnectionState = false; }

            //See if the tcp state is ok
            if (_tcpClient.Client.Poll(5000, SelectMode.SelectRead) && (_tcpClient.Client.Available == 0))
            {
                ConnectionState = false;
            }
        }
    }
    else { ConnectionState = false; }
#endregion
1
M Granja
public static class SocketExtensions
{
    private const int BytesPerLong = 4; // 32 / 8
    private const int BitsPerByte = 8;

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


    /// <summary>
    /// Sets the keep-alive interval for the socket.
    /// </summary>
    /// <param name="socket">The socket.</param>
    /// <param name="time">Time between two keep alive "pings".</param>
    /// <param name="interval">Time between two keep alive "pings" when first one fails.</param>
    /// <returns>If the keep alive infos were succefully modified.</returns>
    public static bool SetKeepAlive(this Socket socket, ulong time, ulong interval)
    {
        try
        {
            // Array to hold input values.
            var input = new[]
            {
                (time == 0 || interval == 0) ? 0UL : 1UL, // on or off
                time,
                interval
            };

            // Pack input into byte struct.
            byte[] inValue = new byte[3 * BytesPerLong];
            for (int i = 0; i < input.Length; i++)
            {
                inValue[i * BytesPerLong + 3] = (byte)(input[i] >> ((BytesPerLong - 1) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 2] = (byte)(input[i] >> ((BytesPerLong - 2) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 1] = (byte)(input[i] >> ((BytesPerLong - 3) * BitsPerByte) & 0xff);
                inValue[i * BytesPerLong + 0] = (byte)(input[i] >> ((BytesPerLong - 4) * BitsPerByte) & 0xff);
            }

            // Create bytestruct for result (bytes pending on server socket).
            byte[] outValue = BitConverter.GetBytes(0);

            // Write SIO_VALS to Socket IOControl.
            socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true);
            socket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue);
        }
        catch (SocketException)
        {
            return false;
        }

        return true;
    }
}
  1. Copiez la classe SocketExtensions dans votre projet.
  2. Appelez SetKeepAlive sur votre socket - socket.SetKeepAlive (1000, 2);
  3. Ajouter une minuterie pour vérifier la fonction IsConnected
1
Amir Twito

La meilleure solution consiste simplement à ce que votre client envoie une PING toutes les X secondes et au serveur de supposer qu'il est déconnecté après ne pas en avoir reçu pendant un certain temps.

J'ai rencontré le même problème que vous lorsque vous utilisiez des sockets, et c'était le seul moyen de le faire. La propriété socket.connected n'a jamais été correcte.

En fin de compte, cependant, je suis passé à l’utilisation de WCF, qui était beaucoup plus fiable que les sockets.

1
NibblyPig

Comme Alexander Logger a souligné dans zendar s, vous devez envoyer quelque chose pour être complètement sûr. Si votre partenaire connecté lit sur cette socket, vous pouvez utiliser le code suivant.

bool SocketConnected(Socket s)
{
  // Exit if socket is null
  if (s == null)
    return false;
  bool part1 = s.Poll(1000, SelectMode.SelectRead);
  bool part2 = (s.Available == 0);
  if (part1 && part2)
    return false;
  else
  {
    try
    {
      int sentBytesCount = s.Send(new byte[1], 1, 0);
      return sentBytesCount == 1;
    }
    catch
    {
      return false;
    }
  }
}

Mais même dans ce cas, cela peut prendre quelques secondes avant qu’un câble réseau cassé ou quelque chose de similaire soit détecté.

0
Jrabauer