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

发布于 2024-08-28 23:23:23 字数 88 浏览 2 评论 0原文

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

How can you check if a network socket (System.Net.Sockets.Socket) is still connected if the other host doesn't send you a packet when it disconnects (e.g. because it disconnected ungracefully)?

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(11

无风消散 2024-09-04 23:23:23

正如 Paul Turner 回答的那样,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 如果
    • 连接已关闭、重置、终止或待处理(表示没有活动连接)
    • 连接处于活动状态并且有数据可供读取
  • /library/system.net.sockets.socket.available.aspx" rel="noreferrer">s.Available如果两者都为 true, :
    • 没有可供读取的数据,因此连接未处于活动状态

As Paul Turner answered Socket.Connected cannot be used in this situation. You need to poll connection every time to see if connection is still active. This is code I used:

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

It works like this:

  • s.Poll returns true if
    • connection is closed, reset, terminated or pending (meaning no active connection)
    • connection is active and there is data available for reading
  • s.Available returns number of bytes available for reading
  • if both are true:
    • there is no data available to read so connection is not active
夏日落 2024-09-04 23:23:23

正如 zendar 所写,使用 Socket.Poll 和 Socket 很好。可用,但您需要考虑到套接字可能尚未初始化。这是最后一条信息(我相信),它由 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;

*/
    }

As zendar wrote, it is nice to use the Socket.Poll and Socket.Available, but you need to take into consideration that the socket might not have been initialized in the first place. This is the last (I believe) piece of information and it is supplied by the Socket.Connected property. The revised version of the method would looks something like this:

 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;

*/
    }
Hello爱情风 2024-09-04 23:23:23

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

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

您可以在异常发生后检查该属性,但在发生异常之前它并不可靠。

The Socket.Connected property will tell you whether a socket thinks it's connected. It actually reflects the status of the last send/receive operation performed on the socket.

If the socket has been closed by your own actions (disposing the socket, calling methods to disconnect), Socket.Connected will return false. If the socket has been disconnected by other means, the property will return true until you next attempt to send or recieve information, at which point either a SocketException or ObjectDisposedException will be thrown.

You can check the property after the exception has occurred, but it's not reliable before.

蓝眼泪 2024-09-04 23:23:23

如果拔掉网线,接受的答案似乎不起作用。或者服务器崩溃。或者你的路由器崩溃了。或者如果您忘记支付互联网账单。设置 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 秒检测。之后,套接字上的任何读取/写入/轮询操作都将失败。

The accepted answer doesn't seem to work if you unplug the network cable. Or the server crashes. Or your router crashes. Or if you forget to pay your internet bill. Set the TCP keep-alive options for better reliability.

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

The time value sets the timeout since data was last sent. Then it attempts to send and receive a keep-alive packet. If it fails it retries 10 times (number hardcoded since Vista AFAIK) in the interval specified before deciding the connection is dead.

So the above values would result in 2+10*1 = 12 second detection. After that any read / wrtie / poll operations should fail on the socket.

○愚か者の日 2024-09-04 23:23:23
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 函数
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. Copy the SocketExtensions class to your project
  2. Call the SetKeepAlive on your socket - socket.SetKeepAlive(1000, 2);
  3. Add a timer to check the IsConnected function
江湖正好 2024-09-04 23:23:23

我基于 this 制作了一个扩展方法 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;
    }
}

I made an extension method based on this MSDN article.
This is how you can determine whether a socket is still connected.

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;
    }
}
自此以后,行同陌路 2024-09-04 23:23:23

正如 Alexander Loggerzendar的回答,你必须发送一些东西才能完全确定。
如果您的关联伙伴
不在此套接字上读取
所有,您可以使用以下
代码。

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

但即便如此,也可能需要几秒钟的时间才能检测到网络电缆损坏或类似情况。

As Alexander Logger pointed out in zendars answer, you have to send something to be completely sure.
In case your connected partner
does not read on this socket at
all, you can use the following
code.

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

But even then it might take a few seconds until a broken network cable or something similar is detected.

两人的回忆 2024-09-04 23:23:23

遵循 NibblyPig的建议zendar,我想出了下面的代码,它适用于我所做的每个测试。我最终需要 ping 和 poll。 ping 将让我知道电缆是否已断开,或者物理层是否已中断(路由器断电等)。但有时重新连接后我会收到 RST,ping 正常,但 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

Following the advice from NibblyPig and zendar, I came up with the code below, which works on every test I made. I ended up needing both the ping, and the poll. The ping will let me know if the cable has been disconnected, or the physical layer otherwise disrupted (router powered off, etc). But sometimes after reconnect I get a RST, the ping is ok, but the tcp state is not.

#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
盗琴音 2024-09-04 23:23:23

最好的方法就是让您的客户端每 X 秒发送一次 PING,并且服务器在一段时间内没有收到 PING 消息后假设它已断开连接。

我在使用套接字时遇到了与您相同的问题,这是我能做到的唯一方法。 socket.connected 属性从来都不正确。

但最终,我转而使用 WCF,因为它比套接字可靠得多。

The best way is simply to have your client send a PING every X seconds, and for the server to assume it is disconnected after not having received one for a while.

I encountered the same issue as you when using sockets, and this was the only way I could do it. The socket.connected property was never correct.

In the end though, I switched to using WCF because it was far more reliable than sockets.

复古式 2024-09-04 23:23:23

只需像 @toster-cx 所说的那样使用 KeepAlive,然后使用 Socket Connected 状态来检查 Socket 是否仍处于连接状态。将接收超时设置为与保持连接超时相同。如果您有更多问题,我很乐意为您提供帮助!

Just use the KeepAlive like @toster-cx says and then use the Socket Connected status to check if the Socket is still connected. Set your receive timeout at the same timeout of the keepalive. If you have more questions i am always happy to help!

╰◇生如夏花灿烂 2024-09-04 23:23:23

使用 Socket.Connected 属性。

--更新--

正如 Paul Turner 所回答的那样,在这种情况下不能使用 Socket.Connected 。您需要每次轮询连接以查看连接是否仍然活动。请参阅2

Use Socket.Connected Property.

--UPDATE--

As Paul Turner answered Socket.Connected cannot be used in this situation. You need to poll connection every time to see if connection is still active. See 2

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文