Подтвердить что ты не робот

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

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

4b9b3361

Ответ 1

Как Пол Тернер ответил 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, если
    • соединение закрыто, reset, завершено или ожидает (что означает отсутствие активного соединения)
    • активно и доступны данные для чтения
  • s.Available возвращает количество байтов, доступных для чтения
  • если оба истины:
    • нет данных для чтения, поэтому соединение неактивно

Ответ 2

Как 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;

*/
    }

Ответ 3

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

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

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

Ответ 4

Принятый ответ не работает, если вы отключите сетевой кабель. Или сервер сбой. Или ваш маршрутизатор падает. Или если вы забудете оплатить свой интернет-счет. Задайте параметры сохранения 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);

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

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

Ответ 5

Я сделал метод расширения, основанный на этой статье 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;
    }
}

Ответ 6

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

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

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

Ответ 7

Следуя советам NibblyPig и zendar, я придумал код ниже, который работает на каждом испытании, которое я сделал. В итоге мне потребовались как пинг, так и опрос. Пинг сообщит мне, отсоединен ли кабель или физический уровень в противном случае нарушен (отключен маршрутизатор и т.д.). Но иногда после повторного подключения я получаю RST, ping в порядке, но состояние tcp не является.

#region CHECKS THE SOCKET 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

Ответ 8

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;
    }
}
  • Скопировать класс SocketExtensions в ваш проект
  • Вызовите SetKeepAlive на свой сокет - socket.SetKeepAlive(1000, 2);
  • Добавить таймер для проверки функции IsConnected