Website-Icon Digital Native

C# – Verbindungsstatus eines Sockets feststellen – Teil 2

Ich schrieb schon zuvor einmal darüber, wie ich den Verbindungsstatus eines C#-Sockets überprüfe. Während der Testphase eines neuen Projekts, bei dem ich viele Mobilgeräte gleichzeitig über verschiedene Mobilfunktechnologien steuere, musste ich periodisch überprüfen, ob die verbundenen Clients überhaupt noch ansprechbar waren oder nicht. Also nutzte ich meine erprobte IsConnected-Methode und staunte nicht schlecht, als mir Code steif und fest behauptete, dass meine Verbindung noch bestünde, obwohl die Gegenstelle zu Testzwecken komplett ausgeschaltet wurde. Ich konnte sogar Daten in den Socket schreiben, ohne dass es Fehler hagelte. Klasse.

Ich erspare euch jetzt die wortreiche Beschreibung wie ich tagelang auf den Code starrte und meine Fingernägel rillen im Tisch hinterließen und mache es kurz: Erst nachdem ich das KeepAlive-Sendeintervall des Sockets drastisch heruntersetzte, funktionierte auch wieder meine IsConnected-Überprüfung wieder.

Hierzu fand ich im Internet die folgende Extension-Methode die, einmal im Namespace geladen, ihre Funktionalität jedem Socket-Objekt zur Verfügung stellt.

namespace System.Net.Sockets
{
  public static class SocketExtensions
  {
    private const int BytesPerLong = 4; // 32 / 8
    private const int BitsPerByte = 8;

    /// <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.Tcp, SocketOptionName.KeepAlive, true);
        socket.IOControl(IOControlCode.KeepAliveValues, inValue, outValue);
      }
      catch (SocketException e)
      {
        Console.WriteLine("Failed to set keep-alive: {0} {1}", e.ErrorCode, e);
        return false;
      }

      return true;
    }
  }
}

Die Benutzung des Codes ist sehr einfach:

//Create socket
var _socket = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
//Connect to any host
_socket.Connect(IPAddress.Parse("127.0.0.1"), 80);
//Set KeepAlive
_socket.SetKeepAlive(250, 100);

Mein Fazit?  Ich muss dringend von den Sockets wegkommen und etwas nutzen das mehr „High-level“ ist. Wenn ich weiterhin mit Sockets arbeite ist das einzige was „High-level“ ist nur mein Blutdruck.

Die mobile Version verlassen