如何在C#中检查插座是否连接/断开?

如何在C#中检查插座是否连接/断开?,c#,sockets,connection,C#,Sockets,Connection,如果另一台主机在断开连接时没有向您发送数据包(例如,由于未正常断开连接),您如何检查网络套接字(System.Net.Sockets.socket)是否仍然连接?使用属性 --更新-- 正如Paul Turner回答的,Socket.Connected不能在这种情况下使用。您需要每次轮询连接以查看连接是否仍处于活动状态。请参见属性将告诉您套接字是否认为已连接。它实际上反映了在套接字上执行的上一次发送/接收操作的状态 public static class SocketExtensions {

如果另一台主机在断开连接时没有向您发送数据包(例如,由于未正常断开连接),您如何检查网络套接字(System.Net.Sockets.socket)是否仍然连接?

使用属性

--更新--

正如Paul Turner回答的,Socket.Connected不能在这种情况下使用。您需要每次轮询连接以查看连接是否仍处于活动状态。请参见

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

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;
    }
}
如果您自己的操作(处理套接字、调用方法断开连接)关闭了套接字,
socket.Connected
将返回
false
。如果套接字已通过其他方式断开连接,则属性将返回
true
,直到您下次尝试发送或接收信息,此时将抛出
SocketException
ObjectDisposedException

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

作为已应答的
套接字。在这种情况下,无法使用已连接的
。您需要每次轮询连接以查看连接是否仍处于活动状态。这是我使用的代码:

bool SocketConnected(Socket s)
{
    bool part1 = s.Poll(1000, SelectMode.SelectRead);
    bool part2 = (s.Available == 0);
    if (part1 && part2)
        return false;
    else
        return true;
}
它的工作原理如下:

 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;

*/
    }
  • 如果
    • 连接关闭、重置、终止或挂起(表示没有活动连接)
    • 连接处于活动状态,并且有数据可供读取
  • 返回可读取的字节数
  • 如果两者均为真:
    • 没有可读取的数据,因此连接未处于活动状态

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

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

      但最后,我改用WCF,因为它比套接字可靠得多。

      如前所述,使用
      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;
      
      */
          }
      

      我在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;
          }
      }
      
      根据和的建议,我提出了下面的代码,它适用于我所做的每个测试。我最终需要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
      

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

      公共静态类SocketExtensions
      
      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;
          }
      }
      
      { private const int BytesPerLong=4;//32/8 私有常量int比特数=8; 公共静态端口已断开连接(此插槽) { 尝试 { return!(socket.Poll(1000,SelectMode.SelectRead)和&socket.Available==0); } 捕获(SocketException) { 返回false; } } /// ///设置套接字的保持活动间隔。 /// ///插座。 ///两次持续“ping”之间的时间。 ///第一次“ping”失败时两次保持活动状态“ping”之间的时间。 ///如果成功修改了“保持活力”信息。 公共静态bool SetKeepAlive(此套接字、ulong时间、ulong间隔) { 尝试 { //数组来保存输入值。 var输入=新[] { (时间==0 | |间隔==0)?0UL:1UL,//打开或关闭 时间 间隔 }; //将输入打包到字节结构中。 字节[]无效=新字节[3*字节/长]; for(int i=0;i>((BytesPerLong-1)*比特字节)&0xff); 无效[i*BytesPerLong+2]=(字节)(输入[i]>>((BytesPerLong-2)*比特字节)&0xff); 无效[i*BytesPerLong+1]=(字节)(输入[i]>>((BytesPerLong-3)*比特字节)&0xff); 无效[i*BytesPerLong+0]=(字节)(输入[i]>>((BytesPerLong-4)*比特字节)和0xff); } //为结果创建ByTestStruct(服务器套接字上挂起的字节)。 byte[]outValue=BitConverter.GetBytes(0); //将SIO_VAL写入套接字IOControl。 socket.SetSocketOption(SocketOptionLevel.socket,SocketOptionName.KeepAlive,true); IOControl(IOControlCode.KeepAliveValues、inValue、outValue); } 捕获(SocketException) { 返回false; } 返回true; } }
    • 将SocketExtensions类复制到项目中
    • 在套接字上调用SetKeepAlive-socket.SetKeepAlive(1000,2)
    • 添加计时器以检查断开连接的功能