# 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) -(原作者如有要求,会立马下架此章节)