检测断开的客户端套接字连接(跨平台适用性)

Detecting disconnected client side socket connection (cross-platform applicability)

我知道这个问题有成百上千的答案,但以我的情况我做不出来。场景是这样的,我们有一个用TCP/IP协议写的服务器,我们有多个客户端连接到这个服务器。这里的客户端是一个软件模块,它在客户端机器上启动之前,在服务器上注册它的存在并加载功能。但问题是这个软件模块崩溃了,并且没有 socket.close() 被调用,这将使得它的足迹仍然存在于服务器中,即使它崩溃了。如何识别?

我在客户端使用 select() 方法来通知来自客户端和服务器的任何信息(反之亦然) 我无法在服务器中为每个客户端请求单独创建一个进程,也无法在客户端机器中创建父子机制。 tcp-keepalive 不适用,因为我们需要调整 Windows 中的注册表?我需要一个 x 平台兼容的解决方案。 我已经读到 recv() 到服务器代码中连接的套接字将 return 来自 'that' 客户端的一些值,例如 0 套接字关闭?我可以用它来清除服务器数据库中的客户端套接字注册吗?这行得通吗?

tcp-keepalive is not applicable as we need to tweak the registry in Windows?

如果您可以接受默认的两小时超时,TCP keepalive 是一个选项。

I need a x-platform compatible solution.

TCP keepalive 是跨平台的。

I have read that recv() to the connected socket in the server code will return some values from 'that' client such as 0 for socket closed?

如果对等方关闭其套接字,并且在某些平台上,如果对等方进程只是退出而不关闭它,它将 return 为零。

Can I use this to clear off the client socket registration in the server database? Will this work?

仅当您可以依赖对端正确关闭套接字时。

在我看来,您应该做的是调试客户端代码以使其不会崩溃,并使用 TCP keepalive 作为长期备份。

您还应注意 send() 到已退出的对等方迟早会因 ECONNRESET 错误而失败。

您没有指定在服务器端代码中使用什么方法来处理套接字事件。无论您使用何种方法轮询您的套接字,当客户端崩溃时,recv() 将 return 0 或 可能 -1/SOCKET_ERROR

为了检测不活动的客户端连接,大多数服务器应用程序会在应用程序层协议中定期发送某种形式的心跳或 ping 消息。当无法从客户端发送 ACK 时,服务器应用程序将通过 recv() returning 0 或 SOCKET_ERROR 收到客户端断开连接的通知,错误代码类似于 WSAENETRESETWSAECONNABORTEDWSAETIMEDOUTWSAECONNRESET(请参阅各种错误代码 here)。通常在服务器将心跳发送到不再活动的客户端 TCP 端口后,会发送一个 ICMP 数据包作为响应,提醒您的服务器端口或主机不活动(recv() 将立即通知您此事件) .

如果你想打开 TCP 保持活动计时器,你可以使用套接字选项 SO_KEEPALIVE. The interval can also be set using SIO_KEEPALIVE_VALS

编辑:请记住各种错误代码和选项 SIO_KEEPALIVE_VALS 是特定于 Win32 的。要为其他操作系统处理这些事件,您将需要使用操作特定的方法来检索错误代码并设置 TCP 保持活动间隔(如果您选择这样做)。保持您的代码跨平台兼容的最佳建议是简单地将应用层心跳消息实施到您的协议或其他一些应用层特定的超时中。这样做会让您忘记管理 TCP keep alives。

更新

我无法评论 EJP 的回答,但重要的是要指出,通过调用 send() 他有效地建议您在协议的应用层中实施 heartbeat/ping 消息。虽然检查 send() 的 return 值很重要,但如果您正在 polling/selecting 读取事件,您将在连接发生时调用 recv() 立即收到 TCP 连接被断开的通知被 TCP 堆栈视为损坏。如果您等待您的应用程序计时器尝试使用 send() 发送一些数据,这可能会在 recv() 已经通知您连接断开后的许多秒(取决于您的间隔计时器的长度)。换句话说:注意 recv() return 值以及您的 send() return 值。