网站首页swoole
websocket心跳包实现
发布时间:2018-09-17 03:36:23编辑:slayer.hover阅读(5023)
应用场景:在APP中用户登陆后,需要向服务器端发报告说明当前在线。在有业务发生时,服务器会向所有在线的特定用户推送消息。
APP里打开websocket连接后,在网络异常情况下,可能会异常断开。所以需要用心跳包来保持长时间的连接。
服务器端使用swoole的swoole_websocket_server实现,用户上线后,将其数据存放于redis
class WS_Server
{
private $serv;
private static $cache;
private static $cache_set = [
'host'=>'127.0.0.1',
'port'=>6379,
'db'=>5
];
public function __construct() {
self::$cache = new \Redis();
self::$cache->connect(self::$cache_set['host'], self::$cache_set['port']);
self::$cache->select(self::$cache_set['db']);
$this->serv = new \swoole_websocket_server("0.0.0.0", 9502);
$this->serv->on('open',[$this,'onOpen']);
$this->serv->on('message',[$this,'onMessage']);
$this->serv->on('close', [$this,'onClose']);
$this->serv->start();
}
public function onMessage($serv, $frame){
if($frame->data=='ping'){
#处理心跳包
$serv->push($frame->fd, 'pong');
}else {
$data = json_decode($frame->data, TRUE);
if ($data) {
switch ($data['action']) {
#新用户上线,接收open数据包
case 'open':
$mem = [
'id' => $data['id'],
'type' => $data['type'],
'name' => $data['name'],
'phone'=> $data['phone'],
];
self::$cache->hset('members', $frame->fd, json_encode($mem));
#清除意外断掉的用户
$allmem = self::$cache->hGetAll('members');
if (!empty($allmem)) {
foreach ($allmem as $k => $v) {
$row = json_decode($v, true);
if ($k != $frame->fd && $row['id'] == $data['id']) {
self::$cache->hdel('members', $k);
}
}
}
break;
#其它具体业务处理
case 'otherEvent':
...
break;
}
}
}
}
public function onClose($serv, $fd){
self::$cache->hDel('members', $fd);
}websocket客户端实现:
var heartCheck = {
timeout: 60000, //心跳间隔
timeoutObj: null,
serverTimeoutObj: null,
reset: function(){
clearTimeout(heartCheck.timeoutObj);
clearTimeout(heartCheck.serverTimeoutObj);
return this;
},
start: function(){
heartCheck.timeoutObj && clearTimeout(heartCheck.timeoutObj);
heartCheck.serverTimeoutObj && clearTimeout(heartCheck.serverTimeoutObj);
heartCheck.timeoutObj = setTimeout(function(){
if(vm.socket && vm.socket.readyState===1) {
vm.socket.send("ping");
heartCheck.serverTimeoutObj = setTimeout(function () {
vm.reconnect();
}, heartCheck.timeout);
}
}, heartCheck.timeout)
}
};
var vm = new Vue({
el: '#app',
data: {
userInfo: null,
webSocket_url: 'ws://127.0.0.1:9502',
socket: null,
lockReconnect: false, //重连标识
jump: null, //心跳
},
methods: {
wsconnect: function () {
try{
vm.socket=new WebSocket(vm.webSocket_url);
}catch(e){
console.log('websocket error');
return false;
}
vm.socket.onmessage= function(msg){
if(msg.data!='pong') {
//根据msg.data处理具体业务
...
}
heartCheck.reset().start();
}
vm.socket.onclose=function(){
vm.reconnect();
}
vm.socket.onerror=function(){
vm.reconnect();
}
vm.socket.onopen=function(){
heartCheck.reset().start();
}
},
reconnect:function(){
if(vm.lockReconnect) {
return false;
};
vm.lockReconnect = true;
vm.jump && clearTimeout(vm.jump);
vm.jump = setTimeout(function () {
vm.wsconnect();
vm.lockReconnect = false;
}, 4000);
},
wsopen:function(){
!vm.socket && vm.wsconnect();
if(vm.socket.readyState===1) {
vm.userInfo = localStorage.getItem('userInfo');
//推送用户上线消息
vm.socket.send(JSON.stringify({
action: 'open',
id: vm.userInfo.id,
type: vm.userInfo.type,
name: vm.userInfo.name,
phone:vm.userInfo.phone,
}));
}else{
setTimeout(function(){
vm.wsopen();
}, 1000);
}
}
}
});
//此页面最好在整个项目中只会执行一次,并且不会关掉
window.onLoad= function(){
//判断用户登陆后,打开websocket连接
if(localStorage.getItem('userInfo')){
vm.wsopen();
}
}经测试,只要App进程没被杀死,基本可保持用户长期在线。
评论