如何检测客户端是否与服务器断开连接?
我的 AcceptCallBack
方法中有以下代码
static Socket handler = null;
public static void AcceptCallback(IAsyncResult ar)
{
//Accept incoming connection
Socket listener = (Socket)ar.AsyncState;
handler = listener.EndAccept(ar);
}
我需要找到一种方法来尽快发现客户端与 handler
套接字断开连接 .
我试过了:
-
handler.Available;
-
handler.Send(new byte[1], 0, SocketFlags.None);
-
handler.Receive(new byte[1], 0, SocketFlags.None);
当您连接到服务器并且想要检测服务器何时断开连接但是当您是服务器并且想要检测客户端断开连接时它们不起作用时,上述方法会起作用 .
任何帮助将不胜感激 .
13 回答
由于在断开套接字时没有可用于发出信号的事件,因此您必须以您可接受的频率轮询它 .
使用此扩展方法,您可以使用可靠的方法来检测套接字是否已断开连接 .
这根本不可能 . 您和服务器之间没有物理连接(极少数情况下,您使用环回电缆连接两个编译器) .
正常关闭连接后,将通知另一方 . 但是如果连接以其他方式断开(比如用户连接被丢弃),那么服务器将不会知道,直到它超时(或尝试写入连接并且确认超时) . 这就是TCP工作的方式,你必须忍受它 .
因此,“即时”是不现实的 . 您可以做的最好的事情是在超时期限内,这取决于代码运行的平台 .
编辑:如果您只是寻找优美的连接,那么为什么不从您的客户端向服务器发送“DISCONNECT”命令呢?
有人提到TCP套接字的keepAlive功能 . 这里描述得很好:
http://tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html
我'm using it this way: after the socket is connected, I' m调用此函数,它将keepAlive设置为on .
keepAliveTime
参数指定超时(以毫秒为单位),在发送第一个保持活动数据包之前没有活动 .keepAliveInterval
参数指定在未收到确认的情况下发送连续保持活动数据包之间的间隔(以毫秒为单位) .我也在使用同步阅读:
在回调中,这里被捕获超时
SocketException
,当套接字在保持活动数据包之后没有得到ACK信号时引发 .这样,我就能安全地检测TCP客户端和服务器之间的断开连接 .
“这就是TCP的工作方式,你必须忍受它 . ”
是的,你是对的 . 这是生活中我已经意识到的事实 . 即使在使用该协议(甚至其他协议)的专业应用程序中,您也会看到相同的行为 . 我甚至看到它出现在网络游戏中;你哥们说“再见”,他似乎又在网上待了1-2分钟,直到服务员“清理房子” .
您可以在此处使用建议的方法,或实施“心跳”,如同建议的那样 . 我选择前者 . 但是,如果我确实选择了后者,我只需让服务器每隔一个字节“ping”一个客户端,看看我们是否有超时或没有响应 . 您甚至可以使用后台线程来实现精确计时 . 如果你真的担心它,甚至可以在某种选项列表(枚举标志或其他东西)中实现组合 . 但是,只要你更新,在更新服务器方面有一点延迟并不是什么大不了的事 . 这是互联网,没有人期望它是魔术! :)
在您的系统中实施心跳可能是一种解决方案 . 只有在客户端和服务器都在您的控制之下时,才能执行此操作 . 您可以使用DateTime对象跟踪从套接字接收最后一个字节的时间 . 并假设套接字未在一定时间间隔内响应丢失 . 这仅在您实施心跳/自定义保持活动时才有效 .
我发现非常有用,另一种解决方法!
如果您使用异步方法从网络套接字读取数据(我的意思是,使用
BeginReceive
-EndReceive
方法),只要连接终止;出现以下情况之一:发送的消息没有数据(您可以使用Socket.Available
查看 - 即使BeginReceive
被触发,其值将为零)或Socket.Connected
值在此调用中变为false(不要尝试使用EndReceive
然后) .我发布了我使用过的功能,我想你可以更好地了解我的意思:
这对我有用,关键是你需要一个单独的线程用轮询来分析套接字状态 . 在与套接字检测失败相同的线程中执行此操作 .
你不能只使用Select吗?
在连接的套接字上使用select . 如果select返回,套接字为Ready,则后续Receive返回0字节,表示客户端断开连接 . AFAIK,这是确定客户端是否断开连接的最快方法 .
我不知道C#所以只要忽略我的解决方案是否适合C#(C#确实提供了select)或者我是否误解了上下文 .
此处的示例代码http://msdn.microsoft.com/en-us/library/system.net.sockets.socket.connected.aspx显示了如何确定Socket是否仍然连接而不发送任何数据 .
如果您在服务器程序上调用了Socket.BeginReceive(),然后客户端“正常”关闭了连接,则将调用您的接收回调,并且EndReceive()将返回0个字节 . 这0个字节意味着客户端“可能”已断开连接 . 然后,您可以使用MSDN示例代码中显示的技术来确定连接是否已关闭 .
使用方法SetSocketOption,您将能够设置KeepAlive,以便在Socket断开连接时通知您
http://msdn.microsoft.com/en-us/library/1011kecd(v=VS.90).aspx
希望能帮助到你!拉米罗里纳尔迪
我有同样的问题,试试这个:
通过mbargiel和mycelo扩展了对已接受答案的注释,以下内容可与服务器端的非阻塞套接字一起使用,以通知客户端是否已关闭 .
这种方法不会受到影响接受答案中的民意调查方法的竞争条件 .
该方法基于以下事实:
Socket.Receive
方法在远程端关闭其套接字后立即返回零,并且我们已从中读取所有数据 . 来自Socket.Receive documentation:第二点解释了try-catch的必要性 .
使用
SocketFlags.Peek
标志会使任何接收的数据保持不变,以便单独的接收机制进行读取 .以上也适用于阻塞套接字,但要注意代码将在接收调用上阻塞(直到接收到数据或接收超时,再次导致
SocketException
) .如果要轮询,还可以检查套接字的.IsConnected属性 .