1.准备工作

1.gateway-worker扩展 (composer仓库中的插件与文档存在一定偏差,所以采用提供的demo包)gateway-worker

2.将下载的文件放在extend目录下(此demo的gateway-worker是3.1.0)

3.windows环境下启动extend文件夹之下的start_for_windows.bat

4.安装gateway-client扩展 –GatewayWorker3.0.8及以上版本请使用 3.0.13版本的GatewayClient

composer require workerman/gatewayclient -v 3.0.13

5.extend\GatewayWorker\vendor\workerman\gateway-worker\src\Lib 该目录下有gateway.php 修改配置

6.注意!!! 业务逻辑都是gatewayclient完成,gatewayworker只做一个启动服务配置的操作,同时需要注意gateway_client的版本和gatewayworker是否适配

2.代码内容

events内容

class Events
{
    /**
     * 当客户端连接时触发
     * 如果业务不需此回调可以删除onConnect
     *
     * @param int $client_id 连接id
     */
    public static function onConnect($client_id)
    {
        $resData = [
            'type' => 'init',
            'client_id' => $client_id,
            'msg' => 'connect is success' // 连接成功
        ];
        Gateway::sendToClient($client_id, json_encode($resData));
    }

    /**
     * 当客户端发来消息时触发
     * @param int $client_id 连接id
     * @param mixed $message 具体消息
     */
    public static function onMessage($client_id, $message)
    {
        //message里需要包含user_id/order_id/client_id,业务参数自己定义
        $msg = json_decode($message,true);
        if (!$msg){
            return '';
        }
        //可能存在一种情况,前端已经连接上了,但是用户一直刷新这个这个页面
        $res = Gateway::getClientIdByUid($msg['user_id']);
        var_dump($res);

        //初始化
        if ($msg['type'] == 'init'){
            $returnMessage = [
                'type' => 'init',
                'content' => 'init',
            ];
            if ($res){//如果绑定过就自动解绑,但是实际测试不解除绑定根据client_id获取到的只有一个数据并不是多个 
                Gateway::unbindUid($res[0],$msg['user_id']);
            }
            Gateway::bindUid($client_id,$msg['user_id']);
        }
        //如果是心跳检测
        if ($msg['type'] == 'heartbeat'){
            $returnMessage = [
                'type' => 'heartbeat',
                'content' => 'heartbeat',
            ];
        }
        //如果是正常数据
        if ($msg['type'] == 'MessageReceived'){
            $returnMessage = [
                'type' => 'MessageReceived',
                'content' => 'MessageReceived',
            ];
        }
        $returnMessage = json_encode($returnMessage);
        Gateway::sendToClient($client_id,$returnMessage);
    }

    /**
     * 当用户断开连接时触发
     * @param int $client_id 连接id
     */
    public static function onClose($client_id)
    {
        $resDatas = [
            'type' => 'colse',
            'client_id' => $client_id,
            'msg' => 'close is success'
        ];
        // 向所有人发送
        GateWay::sendToAll(json_encode($resDatas));
    }
}

重新连接之后client_id并不是多个 如图

模拟前端代码内容

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <meta http-equiv="X-UA-Compatible" content="IE=edge">
    <meta name="viewport" content="width=device-width, initial-scale=1.0">
    <title>Document</title>
</head>
<body>
这里只做接收消息展示用, 发送心跳包和init. 刷新页面可以看到init
init是链接成功展示,发送消息,在event里面可以看到
</body>
<script>
    ws = new WebSocket("ws://"+document.domain+":8282");

    ws.onopen = function() {
        console.log("连接成功");
        var init = JSON.stringify({"type":"init","user_id":"1"});
        ws.send(init);
        //10秒发送一次心跳包
        var heart = JSON.stringify({"type":"heartbeat","user_id":"1"});
        setInterval(function(){
            ws.send(heart);
        },10000);
    };

    // 服务端主动推送消息时会触发这里的onmessage
    ws.onmessage = function(e){
        console.log(e);
        var data = eval("("+e.data+")");
        // console.log(data);
        var type = data.type || '';
        // console.log(type);
        switch(type){
            case 'heartbeat':
                console.log('heartbeat');
                break;
            // 当mvc框架调用GatewayClient发消息时直接打印出来
            case 'MessageReceived':
                // 接收到tp中主动发送数据提示的标记
                console.log('MessageReceived', data);
                break;
            default :
                console.log(e.data);
        }
    };
</script>
</html>

 

也可以这样连接
f12–控制台–输入 –new WebSocket(‘ws://localhost:8282’)

网络-WS-消息-可以看到client_id

 

连接成功如图所示

实际代码中调用

class Message extends Controller
{

    // 发送消息 进行操作
    public function send($serverId)
    {
        require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Protocols/GatewayProtocol.php";
        require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Gateway.php";
        require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Context.php";
        require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/Db.php";
        require_once ROOT_PATH . "extend/GatewayWorker/vendor/workerman/gateway-worker/src/Lib/DbConnection.php";
        $client_id = \GatewayWorker\Lib\Gateway::getClientIdByUid($serverId);
        if ($client_id) {
            //$num = count($client_id);
            $resData = [
                'type' => 'new_order',
                'client_id' => $client_id,
//                'msg' => cdnurl(config('site.order_sound'), true),
                'msg' => 'try this',
            ];
            \GatewayWorker\Lib\Gateway::sendToClient($client_id[0], json_encode($resData));
        }
    }

}

}

重连机制
1.创建一个全局生命周期的定时器(注意只能创建一次,无限制创建定时器客户端会崩)
2.连接建立后,周期性的每30s向服务器发送心跳包,比如ping
3.服务器收到客户端心跳包ping时,回复pong
4.客户端仅记录最后收到pong的时间戳
4.由全局定时器检查网络状况是否良好
if (lastPong + 30 *1.5 < now) {
//超时重连 todo

 

1.连接时候用设备id和客户端id,请求接口绑定;
2.做好心跳
3.做好重连机制(低段位的肯定在onClose注册个断线重连,这种基本就是傻)

 

业务代码案例

//注册
\GatewayClient\Gateway::$registerAddress = '127.0.0.1:1238';
//是否在线
$res =\GatewayClient\Gateway::isOnline($info->client_id);
if ($res == 1){
    \GatewayClient\Gateway::bindUid($info->client_id,$this->auth->id);
    $socket = new Common();
    $data = ['userinfo' => $this->auth->getUser()];
    $data['userinfo']['token'] = $this->auth->getToken();
    $socket->send($this->auth->id, 'scan_login',$data);
}