# 1\. 什么是心跳 其实简单的说就是:客户端隔一段时间就给服务端发送消息,用来告诉服务端这个连接没有断,是正常的,从而维护长连接的持久性。 如果不加心跳包,有的服务器节点(防火墙)会自动把一定时间之内没有数据交互的连接给断掉;而且这中间指不定会有什么乱七八糟的比如机器断电、网线拔出这些幺蛾子出现导致客户端断线。 但是类似断网这种**极端情况**导致客户端断开连接,服务端是不知道的。因为客户端在正常情况下主动断开会向服务端发送一个tcp的fin包。 > 什么是FIN包? > FIN包表示发送端已经达到数据末尾,也就是说双方的数据传送完成,没有数据可以传送了。 > 发送FIN标志 位的TCP数据包后,连接将被断开。 > 这个标志的数据包也经常被用于进行端口扫描。 然而极端情况下,客户端没有机会发这个包,就会导致服务端并不知道客户端断掉了。 # 2\. 长连接必须加心跳 长连接,只要是长连接,长时间不通讯肯定会被防火墙干掉然后断开,服务在非可控情况下断开是非常不好的情况;所以长连接无论如何都要加上心跳包。 # 3\. 心跳实例 接下来以workerman建立websocket连接为实例。 ## 3.1 前端JavaScript代码 在onopen连接建立的时候,定义一个定时器,每10秒钟发送一个包,包的内容随意。 ~~~ var ws = new WebSocket("ws://www.goozp.com"); //连接websocket ws.onopen = function () { setInterval(function () { ws.send('Hello!'); }, 10000) }; ~~~ 一般发送心跳包的间隔在60秒以内。 ## 3.2 Workerman中处理断线 此处参考:[官方文档:心跳](http://doc.workerman.net/315282 "官方文档:心跳") 可以先定义一些常量在之后用到,方便配置: ~~~ define('HEARTBEAT_TIME', 30); // 定义一个心跳间隔30秒 define('CHECK_HEARTBEAT_TIME', 1); // 检查连接的间隔时间 ~~~ 当接收到信息时,我们就记录下接收到信息的时间: ~~~ $worker->onMessage = function($connection, $msg) { // 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间 $connection->lastMessageTime = time(); // TODO 其它业务逻辑... }; ~~~ 在进程启动后设置一个定时器,每隔一段时间遍历一遍当前worker的所有连接 ~~~ // 进程启动后设置一个每秒运行一次的定时器 $worker->onWorkerStart = function($worker) { Timer::add(CHECK_HEARTBEAT_TIME, function()use($worker){ $time_now = time(); foreach($worker->connections as $connection) { // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间 if (empty($connection->lastMessageTime)) { $connection->lastMessageTime = $time_now; continue; } // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接 if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) { $connection->close(); } } }); }; ~~~ 这样,结合前端的心跳包,我们就可以做到维持连接的长久,以及踢出设置时间段内未使用的废弃连接。 >好文转载,此章节转载地址:[咖啡与代码](https://www.goozp.com/category/workerman) -(原作者如有要求,会立马下架此章节)