如何检查一个套接字连接/断开在C#中?

如果一个networking套接字(System.Net.Sockets.Socket)如果另一个主机在断开连接时不发送数据包(例如,因为它非正常断开连接),如何检查networking套接字(System.Net.Sockets.Socket)是否仍然连接?

由于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如果
    • 连接closures,重置,终止或挂起(表示没有活动连接)
    • 连接处于活动状态,并有数据可供读取
  • s.Available返回可用于读取的字节数
  • 如果两者都是真的:
    • 没有数据可读取,因此连接不活跃

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

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

如果套接字已经被自己的行为closures(configuration套接字,调用方法断开连接), Socket.Connected将返回false 。 如果套接字已被其他方式断开连接,则该属性将返回true直到下一次尝试发送或接收信息为止,此时将抛出SocketExceptionObjectDisposedException

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

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

我做了一个基于这个 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; } } 

最好的办法就是让你的客户端每隔X秒发一个PING,服务器假设在没有收到一个PING之后断开连接。

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

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

根据NibblyPig和zendar的build议,我提出了下面的代码,这个代码适用于我所做的每个testing。 我最终需要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 
 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. 添加一个计时器来检查IsConnectedfunction

使用Socket.Connected属性。