为什么需要应用层心跳?Tcp Keepalive 能不能代替心跳?
-
保活
一个客户端连接服务器以后,如果长期没有和服务器有数据来往,可能会被防火墙程序关闭连接,有时候我们并不想要被关闭连接。例如,对于一个即时通讯软件,如果服务器没有消息时,我们确实不会和服务器有任何数据交换,但是如果连接被关闭了,有新消息来时,我们再也没法收到了,这就违背了“即时通讯”的设计要求。 -
检测死链
服务器与某个客户端一般不是位于同一个网络,其之间可能经过数个路由器和交换机,如果其中某个必经路由器或者交换器出现了故障,并且一段时间内没有恢复,导致这之间的链路不再畅通,而此时服务器与客户端之间也没有数据进行交换,由于 TCP 连接是状态机,对于这种情况,无论是客户端或者服务器都无法感知与对方的连接是否正常,这类连接一般称之为“死链”。 -
检测对端应用层状态
心跳除了说明应用程序还活着以外(进程还在,网络通畅),更重要的是表明应用程序还能正常工作。而Tcp keepAlive由操作系统负责探测,即使进程死锁或阻塞,操作系统也会正常收发Tcp Keepalive。
UDP需要设计心跳吗?
UDP是无连接的,所以设计心跳没有意义。
心跳协议设计
其他注意事项
-
要在工作线程发送,不要单独起一个心跳线程
这是防止工作线程死锁活着阻塞时还在继续发心跳 -
与业务消息用同一个心跳连接,不要单独使用 "心跳连接"
心跳消息的作用之一就是验证网络畅通,如果它验证的不是收发业务数据的TCP连接畅通,那就没什么意义。
心跳检测与调试
在开发调试程序过程中,我们可能需要将程序通过断点中断下来,这个过程可能是几秒到几十秒不等。等程序恢复执行时,连接可能因为心跳检测逻辑已经被断开。
调试过程中,我们更多的关注的是业务数据处理的逻辑是否正确,不想被一堆无意义的心跳包数据干扰实线。
鉴于以上两点原因,我们一般在调试模式下关闭或者禁用心跳包检测机制。
#ifndef _DEBUG
EnableHeartCheck();
#endif
心跳检测与日志
实际生产环境,一般会将程序收到的和发出去的数据包写入日志中,但是无业务信息的心跳包信息是个例外,一般会刻意不写入日志,这是因为心跳包数据一般比较多,如果写入日志会导致日志文件变得很大,且充斥大量无意义的心跳包日志,所以一般在写日志时会屏蔽心跳包信息写入。
bool success = WebSocketSession::send(strResponse);
if (success)
{
bool enablePingPongLog = Singleton<Config>::Instance().m_bPingPongLogEnabled;
// 其他消息正常打印,心跳消息按需打印
if (strResponse != "pong" || enablePingPongLog)
{
LOGI("...");
}
}
参考资料
1、http://101.37.25.166/blog/articles/%E9%AB%98%E6%80%A7%E8%83%BD%E6%9C%8D%E5%8A%A1%E5%99%A8%E6%A1%86%E6%9E%B6%E8%AE%BE%E8%AE%A1/%E5%BF%83%E8%B7%B3%E5%8C%85%E6%9C%BA%E5%88%B6%E8%AE%BE%E8%AE%A1%E8%AF%A6%E8%A7%A3.html
2、《Linux 多线程服务器端编程》
网友评论