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

时间:2010-04-18 09:35:52

标签: c# sockets connection

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

11 个答案:

答案 0 :(得分:89)

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; } 。您需要每次轮询连接以查看连接是否仍处于活动状态。这是我使用的代码:

{{1}}

它的工作原理如下:

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

答案 1 :(得分:23)

正如zendar所写,使用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;

*/
    }

答案 2 :(得分:14)

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

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

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

答案 3 :(得分:5)

我根据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;
    }
}

答案 4 :(得分:4)

如果拔下网线,接受的答案似乎不起作用。或者服务器崩溃。或者您的路由器崩溃了。或者,如果您忘记支付您的网络账单。设置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秒检测。之后,任何read / wrtie / poll操作都应该在套接字上失败。

答案 5 :(得分:1)

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

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

最后,我转而使用WCF,因为它比套接字更可靠。

答案 6 :(得分:1)

根据NibblyPigzendar的建议,我提出了以下代码,该代码适用于我所做的每项测试。我最终需要ping和民意调查。如果电缆断开连接,或物理层中断(路由器断电等),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

答案 7 :(得分:1)

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功能

答案 8 :(得分:0)

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

但即使这样,也可能需要几秒钟才能检测到网络电缆或类似情况。

答案 9 :(得分:-1)

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

答案 10 :(得分:-12)

使用Socket.Connected财产。