Как проверить, подключен ли сокет / отключен в C#?


Как вы можете проверить, если сетевой сокет (System.Net.Sockets.Socket) по-прежнему подключен, если другой хост не отправляет вам пакет, когда он отключается (например, потому, что он отключен нелюбезно)?

11 53

11 ответов:

как Пол Тернер ответил Socket.Connected не может использоваться в этой ситуации. Вам нужно опрашивать соединение каждый раз, чтобы увидеть, если соединение все еще активно. Это код, который я использовал:

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

это работает так:

  • s.Poll возвращает true, если
    • соединение закрыто, сброшено, прервано или ожидает (что означает отсутствие активного соединения)
    • соединение активно и данные доступны для чтения
  • s.Available возвращает количество байтов, доступных для чтения
  • если оба истинны:
    • нет данных, доступных для чтения, поэтому соединение не активно

как zendar написал, приятно использовать Socket.Poll и Socket.Available, но вы должны принять во внимание, что сокет, возможно, не был инициализирован в первую очередь. Это последняя (я считаю) часть информации, и она предоставляется Socket.Connected собственность. Пересмотренная версия метода будет выглядеть примерно так:

 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;

*/
    }

The Socket.Connected свойство скажет вам, является ли сокет думает это связано. Он фактически отражает состояние последней операции отправки / получения, выполненной на сокете.

если сокет был закрыт вашими собственными действиями (удаление сокета, вызов методов для отключения),Socket.Connected вернутся false. Если сокет был отключен другим способом, свойство вернет true до следующей попытки отправить или получить информация, в какой момент либо a SocketException или ObjectDisposedException будет брошен.

вы можете проверить свойство после того, как произошло исключение, но оно не является надежным раньше.

принятый ответ, похоже, не работает, если вы отключите сетевой кабель. Или сервер аварийно завершает работу. Или ваш маршрутизатор аварийно завершает работу. Или если вы забыли оплатить счет за интернет. Установите параметры TCP keep-alive для повышения надежности.

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);

значение времени задает время ожидания с момента последней отправки данных. Затем он пытается отправить и получить контрольный пакет. Если это не удается, он повторяет попытку 10 раз (номер жестко закодирован с Vista AFAIK) в интервале, указанном до принятия решения о подключении мертв.

таким образом, приведенные выше значения приведут к 2+10*1 = 12 секунд обнаружения. После этого любые операции чтения / wrtie / poll должны завершиться неудачей на сокете.

Я сделал метод расширения на основе этой статья MSDN. Таким образом можно определить, подключен ли сокет по-прежнему.

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;
    }
}

лучший способ-просто заставить вашего клиента отправлять пинг каждые X секунд, и для сервера предположить, что он отключен после того, как он не получил его некоторое время.

Я столкнулся с той же проблемой, что и вы при использовании сокетов, и это был единственный способ, которым я мог это сделать. Розетка.подключенное свойство никогда не было правильным.

в конце концов, я переключился на использование WCF, потому что он был гораздо более надежным, чем сокеты.

следуя совету от NibblyPig и zendar, Я придумал код ниже, который работает на каждом тесте, который я сделал. В итоге мне понадобился и пинг, и опрос. Пинг даст мне знать, если кабель был отключен, или физический уровень в противном случае нарушен (маршрутизатор выключен и т. д.). Но иногда после повторного подключения я получаю первый, пинг в порядке, но состояние 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
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. скопируйте класс SocketExtensions в свой проект
  2. вызовите SetKeepAlive на вашем сокет-сокете.SetKeepAlive(1000, 2);
  3. добавить таймер для проверки IsConnected функции

Как Александр Логгер указал на zendars ответ, вы должны отправить что-то, чтобы быть полностью уверенным. Если ваш подключенный партнер вообще читает этот сокет, вы можете использовать следующий код.

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;
    }
  }
}

но даже тогда, это может занять несколько секунд, пока не обнаружил обрыв сетевого кабеля или что-то подобное.

просто используйте KeepAlive, как говорит @toster-cx, а затем используйте состояние подключения сокета, чтобы проверить, подключен ли сокет. Набор Вы получаете тайм-аут в то же время ожидания функции keepalive. Если у вас есть вопросы я всегда рад помочь!

использовать Socket.Connected собственность.