如何在C#中检查套接字是否已连接/断开?

les*_*rid 59 c# sockets connection

如果其他主机在断开连接时没有向您发送数据包(例如因为它不正常地断开连接),如何检查网络套接字(System.Net.Sockets.Socket)是否仍然连接?

zen*_*dar 91

由于保罗特纳 回答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;
}
Run Code Online (Sandbox Code Playgroud)

它的工作原理如下:

  • s.Poll 如果返回true
    • 连接已关闭,重置,终止或挂起(表示没有活动连接)
    • 连接处于活动状态,并且有可供读取的数据
  • s.Available 返回可用于读取的字节数
  • 如果两者都是真的:
    • 没有可读数据,因此连接无效

  • 根据`Poll`方法的MSDN页面:`这种方法无法检测某些类型的连接问题,例如网络电缆损坏,或者远程主机被非正常关闭.您必须尝试发送或接收数据以检测这些类型的错误 (7认同)
  • 作为一个单行:`return!(socket.Poll(1,SelectMode.SelectRead)&& socket.Available == 0)` (6认同)
  • 当您连接套接字时,这会失败.如果你做"Socket s = new Socket(AddressFamily.InterNetwork,SocketType.Stream,ProtocolType.Tcp);" 然后是MessageBox.Show(SocketConnected(s)); 你会得到真的.请参见http://stackoverflow.com/a/14925438/741850 (4认同)
  • 再次阅读文档,如果连接关闭或挂起,它也会返回true,因此除非您检查其他方是否向您发送了一些数据,否则您不知道它是否处于活动状态. (3认同)
  • 请注意,这不是线程安全的!很容易发生另一个线程在s.Poll和s.Available之间读取数据的情况,因此s.Poll返回true,但随后缓冲区被清空,导致part2也变为true,从而错误地假定客户端已断开连接。因此,您需要将这段代码和每个s.Receive包装在一个锁{...}或类似的容器中-或最好的方法是什么? (3认同)
  • 这对我不起作用...当客户端被非正常关闭时,part1 = false而part2 = true在服务器端,因此SocketConnected返回true. (2认同)

Aut*_*ico 24

作为禅达写道,这是不错的使用Socket.PollSocket.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;

*/
    }
Run Code Online (Sandbox Code Playgroud)


Pau*_*ner 15

Socket.Connected属性将告诉您套接字是否认为它已连接.它实际上反映了在套接字上执行的最后一次发送/接收操作的状态.

如果套接字已被您自己的操作关闭(处理套接字,调用方法断开连接),Socket.Connected将返回false.如果套接字已通过其他方式断开连接,则该属性将返回,true直到您下次尝试发送或接收信息为止,此时将抛出SocketExceptionObjectDisposedException将被抛出.

您可以在发生异常后检查属性,但之前不可靠.


tos*_*-cx 10

如果您拔掉网络电缆,接受的答案似乎不起作用。或者服务器崩溃。或者你的路由器崩溃了。或者,如果您忘记支付互联网账单。设置 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);
Run Code Online (Sandbox Code Playgroud)

时间值设置自上次发送数据以来的超时时间。然后它尝试发送和接收一个保持活动的数据包。如果失败,它会在确定连接失效之前指定的时间间隔内重试 10 次(自 Vista AFAIK 以来的硬编码次数)。

因此,上述值将导致 2+10*1 = 12 秒的检测。之后,套接字上的任何读取/写入/轮询操作都应该失败。

  • 将 **KeepAlivesValues** 与 Zondar 的建议 (/sf/answers/186331351/) 结合使用,让我能够检测到甚至毫秒的间歇性。 (3认同)
  • 它不会自动重新连接。这只是一种在不发送任何数据的情况下检测连接何时断开的“更好”方法。 (2认同)

Kim*_*Won 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;
    }
}
Run Code Online (Sandbox Code Playgroud)

  • 看起来不错,但这对我不起作用。如果在建立连接后仅拔下网络电缆,则此方法将永远返回_true_。 (2认同)

Ami*_*ito 5

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;
    }
}
Run Code Online (Sandbox Code Playgroud)
  1. 将 SocketExtensions 类复制到您的项目中
  2. 在您的套接字上调用 SetKeepAlive - socket.SetKeepAlive(1000, 2);
  3. 添加一个定时器来检查 IsConnected 函数