立即检测客户端从服务器插座断开连接
如何检测客户端与我的服务器断开连接?
我的AcceptCallBack
方法中有以下代码
static Socket handler = null; public static void AcceptCallback(IAsyncResult ar) { //Accept incoming connection Socket listener = (Socket)ar.AsyncState; handler = listener.EndAccept(ar); }
我需要找到一种方法来尽快发现客户端与handler
Socket断开连接。
我试过了:
-
handler.Available;
-
handler.Send(new byte[1], 0, SocketFlags.None);
-
handler.Receive(new byte[1], 0, SocketFlags.None);
当你连接到一个服务器并想要检测服务器什么时候断开,但是当你是服务器并且想要检测客户端断开时它们不工作时,上述方法是有效的。
任何帮助将不胜感激。
由于当插座断开连接时,没有可用的信号发出,所以您必须以您所能接受的频率进行轮询。
使用这种扩展方法,可以有一个可靠的方法来检测套接字是否断开连接。
static class SocketExtensions { public static bool IsConnected(this Socket socket) { try { return !(socket.Poll(1, SelectMode.SelectRead) && socket.Available == 0); } catch (SocketException) { return false; } } }
这根本不可能。 您和服务器之间没有物理连接(除非在极少数情况下使用回送电缆连接两台计算机)。
当连接正常关闭时,通知另一方。 但是,如果连接断开连接(例如,用户连接断开),服务器将不知道,直到它超时(或试图写入连接和确认超时)。 这就是TCP的工作方式,你必须忍受它。
所以“即刻”是不现实的。 你可以做的最好的是在超时期限内,这取决于代码运行的平台。
编辑:如果你只是寻找优雅的连接,那么为什么不直接发送一个“DISCONNECT”命令从客户端的服务器?
“这只是TCP的工作方式,你必须忍受它。”
是的,你是对的。 这是我意识到的生活中的事实。 即使在使用此协议的专业应用程序(甚至其他)中,您也会看到相同的行为。 我甚至看到它出现在网络游戏中; 你好友说“再见”,他似乎在网上另外1-2分钟,直到服务器“打扫房子”。
您也可以在这里使用建议的方法,或者也可以实施“心跳”。 我选择前者。 但是如果我确实选择了后者,那么我只需要让服务器每隔一个字节就每个客户端“ping”一下,看看我们是否有超时或没有响应。 你甚至可以使用后台线程来实现这个精确的时间。 如果你真的担心的话,甚至可能在某种选项列表(枚举标志或其他)中实现组合。 但是更新服务器稍微有一点延迟就没有什么大不了的了,只要你更新。 这是互联网,没有人期待它是魔术! 🙂
有人提到了TCP Socket的keepAlive功能。 在这里很好地描述:
http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
我这样使用它:套接字连接后,我调用这个函数,它设置keepAlive。 keepAliveTime
参数指定超时(以毫秒为单位),直到发送第一个保持活动的数据包为止,这个超时时间没有活动。 keepAliveInterval
参数指定在未收到确认的情况下发送连续保持活动数据包的时间间隔(以毫秒为单位)。
void SetKeepAlive(bool on, uint keepAliveTime , uint keepAliveInterval ) { int size = Marshal.SizeOf(new uint()); var inOptionValues = new byte[size * 3]; BitConverter.GetBytes((uint)(on ? 1 : 0)).CopyTo(inOptionValues, 0); BitConverter.GetBytes((uint)time).CopyTo(inOptionValues, size); BitConverter.GetBytes((uint)interval).CopyTo(inOptionValues, size * 2); socket.IOControl(IOControlCode.KeepAliveValues, inOptionValues, null); }
我也使用同步阅读:
socket.BeginReceive(packet.dataBuffer, 0, 128, SocketFlags.None, new AsyncCallback(OnDataReceived), packet);
并且在回调中,这里被捕获的超时SocketException
,当套接字在保活信息包之后没有得到ACK信号时引发。
public void OnDataReceived(IAsyncResult asyn) { try { SocketPacket theSockId = (SocketPacket)asyn.AsyncState; int iRx = socket.EndReceive(asyn); catch (SocketException ex) { SocketExceptionCaught(ex); } }
这样,我可以安全地检测TCP客户端和服务器之间的连接。
在你的系统中实现心跳可能是一个解决方案。 只有当客户端和服务器都在你的控制之下时,这才是可能的。 您可以让DateTime对象跟踪从套接字接收到最后一个字节的时间。 并假设在一定的时间间隔内没有响应的套接字丢失。 这只有在心跳/定制保持生效时才有效。
这工作对我来说,关键是你需要一个单独的线程来分析套接字状态与轮询。 在与套接字失败检测相同的线程中执行此操作。
//open or receive a server socket - TODO your code here socket = new Socket(....); //enable the keep alive so we can detect closure socket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, true); //create a thread that checks every 5 seconds if the socket is still connected. TODO add your thread starting code void MonitorSocketsForClosureWorker() { DateTime nextCheckTime = DateTime.Now.AddSeconds(5); while (!exitSystem) { if (nextCheckTime < DateTime.Now) { try { if (socket!=null) { if(socket.Poll(5000, SelectMode.SelectRead) && socket.Available == 0) { //socket not connected, close it if it's still running socket.Close(); socket = null; } else { //socket still connected } } } catch { socket.Close(); } finally { nextCheckTime = DateTime.Now.AddSeconds(5); } } Thread.Sleep(1000); } }
我发现相当有用,另一个解决方法!
如果使用异步方法从网络套接字读取数据(我的意思是使用BeginReceive
– EndReceive
方法),则每当连接终止时; 其中一种情况出现:无论是发送一个没有数据的消息(你可以用Socket.Available
看到它 – 即使BeginReceive
被触发,它的值将为零),或者Socket.Connected
值在这个调用中变成false(不要然后尝试使用EndReceive
)。
我发布我使用的功能,我想你可以更好地看到我的意思:
private void OnRecieve(IAsyncResult parameter) { Socket sock = (Socket)parameter.AsyncState; if(!sock.Connected || sock.Available == 0) { // Connection is terminated, either by force or willingly return; } sock.EndReceive(parameter); sock.BeginReceive(..., ... , ... , ..., new AsyncCallback(OnRecieve), sock); // To handle further commands sent by client. // "..." zones might change in your code. }
你不能只使用选择?
使用连接的套接字选择。 如果select返回的套接字为Ready,但随后的Receive返回0字节,表示客户端断开连接。 AFAIK,这是确定客户端是否断开的最快方式。
我不知道C#所以只是忽略,如果我的解决方案不适合C#(C#提供选择虽然),或者如果我误解了上下文。
如果您在服务器程序上调用Socket.BeginReceive(),然后客户端“优雅地”关闭连接,则您的接收回调将被调用,并且EndReceive()将返回0字节。 这些0字节意味着客户端“可能”已经断开。 然后,您可以使用MSDN示例代码中显示的技术来确定连接是否已关闭。
使用方法SetSocketOption,你将能够设置KeepAlive,让你知道什么时候一个套接字断开连接
Socket _connectedSocket = this._sSocketEscucha.EndAccept(asyn); _connectedSocket.SetSocketOption(SocketOptionLevel.Socket, SocketOptionName.KeepAlive, 1);
http://msdn.microsoft.com/en-us/library/1011kecd(v=VS.90).aspx
希望它有帮助! 拉米罗Rinaldi
如果要轮询,还可以检查套接字的.IsConnected属性。