Как проверить, если сокет подключен / отключен в C #? - PullRequest
59 голосов
/ 18 апреля 2010

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

Ответы [ 11 ]

89 голосов
/ 18 апреля 2010

Как Пол Тернер ответил 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 возвращает количество байтов, доступных для чтения
  • если оба факта верны:
    • нет данных для чтения, поэтому соединение не активно
23 голосов
/ 17 февраля 2013

Как писал 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;

*/
    }
14 голосов
/ 18 апреля 2010

Свойство Socket.Connected сообщит вам, считает ли сокет , что он подключен. Фактически он отражает состояние последней операции отправки / получения, выполненной на сокете.

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

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

5 голосов
/ 20 апреля 2016

Я сделал метод расширения на основе этой статьи 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;
    }
}
4 голосов
/ 23 сентября 2016

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

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 секунд. После этого любые операции чтения / записи / опроса должны завершиться ошибкой в ​​сокете.

1 голос
/ 18 октября 2017
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 на вашем сокете - socket.SetKeepAlive (1000, 2);
  3. Добавить таймер для проверки функции IsConnected
1 голос
/ 26 августа 2016

Следуя советам NibblyPig и zendar , я придумал приведенный ниже код, который работает на каждом моем тесте. В итоге мне понадобился и пинг, и опрос. Пинг сообщит мне, был ли кабель отключен или физический уровень нарушен (питание маршрутизатора выключено и т. Д.). Но иногда после переподключения я получаю RST, пинг в порядке, а состояние 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 голос
/ 18 апреля 2010

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

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

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

0 голосов
/ 03 мая 2018

Просто используйте KeepAlive, как говорит @ toster-cx, а затем используйте статус Socket Connected, чтобы проверить, подключен ли Socket. Установите тайм-аут для получения в то же время для активности активности. Если у вас есть еще вопросы, я всегда рад помочь!

0 голосов
/ 30 апреля 2018

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...