网站首页swoole
websocket心跳包实现
发布时间:2018-09-17 03:36:23编辑:slayer.hover阅读(4324)
应用场景:在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进程没被杀死,基本可保持用户长期在线。
评论