【发布时间】:2011-10-15 01:01:24
【问题描述】:
是否会发生套接字客户端关闭连接而服务器不知道这一点的情况? 我在 C# 中的事件 BeginReceive() 上收到了几个错误。 如何检查客户端是否还活着以及在我的程序的哪些地方进行检查会更好?
【问题讨论】:
标签: c# sockets networking network-programming
是否会发生套接字客户端关闭连接而服务器不知道这一点的情况? 我在 C# 中的事件 BeginReceive() 上收到了几个错误。 如何检查客户端是否还活着以及在我的程序的哪些地方进行检查会更好?
【问题讨论】:
标签: c# sockets networking network-programming
假设您谈论的是基于 TCP 的协议,是的,它可能会发生。 TCP 不需要发送任何数据包来保持连接处于活动状态。因此,如果关闭连接的数据包没有通过服务器,服务器可能会认为客户端仍然存在,即使在客户端关闭连接很久之后。
因为这是 TCP 的一个众所周知的属性,所以在 TCP 之上的每个协议都必须考虑到这一点。所以如果你只是简单地遵循协议,你不会有问题的。
如果协议设计得非常糟糕,或者您正在设计协议,则例外。如果你正在设计协议,你有很多选择。例如,您可以指定一端必须至少每 10 分钟发送一次某种消息。并且您可以指定如果20分钟内没有收到任何消息,另一端应该关闭连接。
【讨论】:
使用套接字编程时,有几种“关闭连接”。 (我将使用 BSD 派生的系统调用名称进行套接字操作,我希望它们通过对 C# 的更改保持相似的名称。)
客户端程序使用shutdown() 或close() 关闭套接字。发生这种情况时,TCP 将向您的服务器发送一个FIN 数据包。当您尝试读取超出客户端发送的最后一个数据字节时,这将在您的服务器中显示为关闭的套接字。下一个read() 可能会抛出异常或错误情况。 (因环境而异。)此条件通常适用于轮询式接口(例如 select(2) 或 poll(2)),因此您的标准事件循环处理程序可以发现客户端退出并处理它们。
客户端程序可能会意外退出。在这种情况下,客户端的 TCP 实现可能会发送FIN 数据包,但它可能不在应用程序输入解析器中的“预期”位置。准备好处理指示应用程序提前终止的零字节读取或EOF 读取。
客户端主机可能会意外死亡。在这种情况下,客户端的 TCP 实现将无法向您的服务器发送FIN 数据包,并且您根本不会收到客户端连接已终止的任何通知。您可以启用 TCP SO_KEEPALIVE 套接字选项,但至少 Linux 默认在发送 keepalive 探测之前系统范围内有两个小时不活动的时间。
如果系统“快速”恢复,您的应用程序将收到 TCP RST 数据包作为响应,或者如果 stateful firewall 配置为 DROP匹配允许或发起的流量流。与FIN 数据包类似,可以检测到RST 案例。丢弃数据包的静默只能通过应用程序级的保活数据包方案来检测。
因此,最好在您可以接受的时间范围内构建某种应用程序级别的 keepalive ping 数据包。 (例如,IRC 使用PING 和PONG 数据包来确保客户端仍然连接到服务器——检查之间的时间可以由网络管理员配置。)
ping 之间的间隔时间以及要接受多少失败的 ping 数据包在很大程度上取决于您的应用程序 - 您可能愿意等待 20 分钟,然后才注意到并删除断开连接的客户端,或者您可能希望注意到并在二十秒后删除断开连接的客户端。多长时间取决于工作环境(20 秒不适用于火星探测器,但对于连接 LAN 的第一人称射击游戏来说可能是 10 倍)和可用于此类“管理开销”的网络带宽。
【讨论】: