如何在C++中实现心跳以检查套接字连接性的最佳方法?

10

大家好。我用sys/socket在C++中编写了客户端和服务器。我需要处理一种情况,即客户端仍处于活动状态,但服务器已关闭。建议使用定期发出心跳以断言连接性的方法来处理这种情况。如果没有连接性,则尝试在X秒内每Y时间段重新连接,然后超时。

这种“心跳”方式是检查连接性的最佳方法吗?

我正在使用的套接字可能有相关信息,有没有一种方法可以在不影响缓冲区的情况下检查是否连接?

5个回答

14
如果您正在使用TCP套接字通过IP网络通信,可以使用TCP协议的保活特性。这将定期检查套接字以确保另一端仍然存在,同时还可以使套接字在客户端和服务器之间的任何NAT路由器中保持转发记录有效。以下是TCP keepalive概述,概述了您可能需要使用TCP keepalive的一些原因; 此Linux特定的HOWTO描述了如何在运行时配置套接字以使用TCP keepalive。
看起来,您可以通过使用WSAIoctl()函数设置SIO_KEEPALIVE_VALS来在Windows套接字中启用TCP keepalive。
如果您正在使用通过IP的UDP套接字进行通信,则需要在您的协议中构建自己的心跳。

2
这是一个非常糟糕的解决方案。TCP保活机制非常不灵活,没有很好的方法控制发送频率。如果你可以控制双方,正如OP所做的那样,在你自己的协议中设计一个连接存活性测试会更好得多。 - David Schwartz
嘿,链接好像有问题 - tldp.org/HOWTO/TCP-Keepalive-HOWTO/overview.html 似乎已经失效。 - Caffeinated
@DavidSchwartz 仅就您反对 TCP keepalives 的建议进行阐述,因为这很少被讨论(因此有些人建议使用它):实际上有一些可以更改的参数,例如“keepalive time”、“keepalive interval”、“number of keepalive probes”,但是这些设置适用于整个系统,而不是每个套接字,即它们不适用于同一系统上的多个用例。此外,在大多数情况下,默认设置都很糟糕,例如保持活动时间为2小时。并且无法以操作系统无关的方式更改它们。 - Evgeniy Berezovsky
从Windows 10(版本1709)开始,支持更多参数(TCP_KEEPIDLE,TCP_KEEPINTVL):https://learn.microsoft.com/en-us/windows/win32/winsock/ipproto-tcp-socket-options - kashiraja

6
是的,这种心跳检测是最好的方式。你需要将其纳入服务器和客户端通信协议中。
最简单的解决方案是让客户端定期发送数据,并且如果服务器在一段特定时间内没有从客户端接收到任何数据,则关闭连接。对于查询/响应协议而言,这个方案非常完美,其中客户端发送查询请求,服务器发送响应。
例如,您可以使用以下方案:
1. 服务器对每个查询都进行响应。如果服务器两分钟内没有收到查询,则关闭连接。
2. 客户端发送查询并在每次查询后保持连接打开状态。
3. 如果客户端连续一分钟都没有发送查询,则发送“你在吗”的查询。服务器响应“是的,我在”。这将重置服务器的两分钟计时器,并向客户端确认连接仍然可用。
也许更简单的方法是,如果客户端在过去一分钟内没有需要发送查询,则直接关闭连接。由于所有操作都是由客户端发起的,因此它始终可以在需要执行新操作时打开新连接。这就缩减为以下内容:
1. 如果服务器两分钟内没有收到查询,则关闭连接。
2. 如果客户端在一分钟内不需要发送查询,则关闭连接。
但是,这不能确保客户端始终能够知道服务器已经存在并准备好随时接受查询。如果您需要这种功能,则必须将“你在吗”和“是的,我在”查询/响应纳入您的协议中。

如果我是提问者,我会将您的帖子标记为答案。 - Wallace

2
如果对方已经消失(即进程已死亡、机器已关闭等),尝试从套接字接收数据应该会导致错误。但是,如果对方仅仅挂起了,套接字将保持打开状态。在这种情况下,拥有心跳是有用的。请确保您正在使用的协议(在TCP之上)支持某种“无操作”请求或数据包-每一方都可以使用此功能来跟踪它们上次从对方收到东西的时间,并且如果数据包之间的时间间隔过长,则可以关闭连接。
请注意,这假设您正在使用TCP/IP。如果您正在使用UDP,则存在完全不同的问题,因为它是无连接的。

1

好的,我不知道你的程序是做什么的,所以也许这不可行,但我建议你避免尝试一直保持套接字开启。它只应该在使用时打开,并在不使用时关闭。

如果你正在等待用户输入而处于读写之间,请关闭套接字。设计你的客户端/服务器协议(假设你是手动设计而不是使用任何标准协议,如http和/或SOAP)来处理这个问题。

如果连接断开,套接字将出现错误;编写你的程序,使得在向套接字写入时不会丢失任何信息,在从套接字读取时出现错误时不会获得任何信息。事务性和原子性应该被纳入你的客户端/服务器协议中(再次假设你正在自己设计它)。


一直关闭和重新打开套接字有什么优势? - Graeme Perrow
你添加了额外的代码和工作,只是为了节省一个socket。虽然Web浏览器过去是这样工作的,但是不停地断开和重新创建连接会导致速度变慢。这就是为什么HTTP/1.1中添加了keepalive的原因。 - Graeme Perrow
保持TCP/IP连接是否打开完全取决于应用程序正在执行的操作。 - 17 of 26
我在一款客户端-服务器数据库产品上工作。我们有一个数据库连接的概念,它有一个基础的TCP连接。在我们的世界中,“试图保持套接字打开”就是不关闭它,在请求之间关闭它将会增加额外的工作量。我认为我正在假设这种范例... - Graeme Perrow
@17 of 26。你说得对。我假设程序是用户交互式的,而不是连续处理的。@Graeme:你也是对的,看看我对@17的评论。不同的范例和所有这些。 :) - Randolpho
显示剩余4条评论

0

网页内容由stack overflow 提供, 点击上面的
可以查看英文原文,
原文链接