如何检查一个套接字连接/断开在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.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; */ }
Socket.Connected
属性会告诉你套接字是否认为已经连接。 它实际上反映了在套接字上执行的最后一次发送/接收操作的状态。
如果套接字已经被自己的行为closures(configuration套接字,调用方法断开连接), Socket.Connected
将返回false
。 如果套接字已被其他方式断开连接,则该属性将返回true
直到下一次尝试发送或接收信息为止,此时将抛出SocketException
或ObjectDisposedException
。
在发生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; } }
- 将SocketExtensions类复制到您的项目
- 调用您的套接字SetKeepAlive – socket.SetKeepAlive(1000,2);
- 添加一个计时器来检查IsConnectedfunction
使用Socket.Connected
属性。