thinkphp6(php)框架结合基于workerman的GatewayWorker框架实现简单即时消息推送demo

目录

1.GatewayWorker的demo

2.thinkphp框架安装GatewayClient  ,在tp项目框架composer.json目录(项目根目录)使用composer运行安装。

3. 配置和启动GatewayWorker

3.1 配置

3.2 按照下图启动GatewayWorker服务

4. 在tp6框架中去使用GatewayClient

5.前端简单demo 


1.GatewayWorker的demo

  •  workerman官网下载demo: gateway-worker 手册
  • 1.2 下载绑定资源
  • 1.3 在我的上传资源中去下载

2.thinkphp框架安装GatewayClient  ,在tp项目框架composer.json目录(项目根目录)使用composer运行安装。

composer require workerman/gatewayclient

3. 配置和启动GatewayWorker

3.1 配置

GatewayWorker/ApplicationsYourApp/start_register.php文件配置内容如下

<?php 
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */
use \Workerman\Worker;
use \GatewayWorker\Register;

// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';

// register 必须是text协议,切记不能将register端口开放给外网
$register = new Register('text://127.0.0.1:1238');

// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
    Worker::runAll();
}

GatewayWorker/ApplicationsYourApp/start_gateway.php 配置文件内容如下

<?php 
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;

// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';

// gateway 进程,这里使用Text协议,可以用telnet测试
$gateway = new Gateway("websocket://0.0.0.0:8282");
// gateway名称,status方便查看
$gateway->name = 'YourAppGateway';
// gateway进程数,一般设置2个就足够
$gateway->count = 2;
// 本机ip,分布式部署时使用内网ip
$gateway->lanIp = '127.0.0.1';
// 内部通讯起始端口,假如$gateway->count=2,起始端口为2900
// 则一般会使用2900 2901 2个端口作为内部通讯端口 
$gateway->startPort = 2900;
// 服务注册地址
$gateway->registerAddress = '127.0.0.1:1238';

// 心跳间隔
//$gateway->pingInterval = 10;
// 心跳数据
//$gateway->pingData = '{"type":"ping"}';

/* 
// 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
$gateway->onConnect = function($connection)
{
    $connection->onWebSocketConnect = function($connection , $http_header)
    {
        // 可以在这里判断连接来源是否合法,不合法就关掉连接
        // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
        if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
        {
            $connection->close();
        }
        // onWebSocketConnect 里面$_GET $_SERVER是可用的
        // var_dump($_GET, $_SERVER);
    };
}; 
*/

// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
    Worker::runAll();
}

GatewayWorker/ApplicationsYourApp/start_businessworker.php配置文件内容如下

<?php 
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */
use \Workerman\Worker;
use \Workerman\WebServer;
use \GatewayWorker\Gateway;
use \GatewayWorker\BusinessWorker;
use \Workerman\Autoloader;

// 自动加载类
require_once __DIR__ . '/../../vendor/autoload.php';

// bussinessWorker 进程
$worker = new BusinessWorker();
// worker名称
$worker->name = 'YourAppBusinessWorker';
// bussinessWorker进程数量
$worker->count = 4;
// 服务注册地址
$worker->registerAddress = '127.0.0.1:1238';

// 如果不是在根目录启动,则运行runAll方法
if(!defined('GLOBAL_START'))
{
    Worker::runAll();
}

 GatewayWorker/ApplicationsYourApp/Events.php内容如下

<?php
/**
 * This file is part of workerman.
 *
 * Licensed under The MIT License
 * For full copyright and license information, please see the MIT-LICENSE.txt
 * Redistributions of files must retain the above copyright notice.
 *
 * @author walkor<walkor@workerman.net>
 * @copyright walkor<walkor@workerman.net>
 * @link http://www.workerman.net/
 * @license http://www.opensource.org/licenses/mit-license.php MIT License
 */

/**
 * 用于检测业务代码死循环或者长时间阻塞等问题
 * 如果发现业务卡死,可以将下面declare打开(去掉//注释),并执行php start.php reload
 * 然后观察一段时间workerman.log看是否有process_timeout异常
 */
//declare(ticks=1);

use \GatewayWorker\Lib\Gateway;

/**
 * 主逻辑
 * 主要是处理 onConnect onMessage onClose 三个方法
 * onConnect 和 onClose 如果不需要可以不用实现并删除
 */
class Events
{
    /**
     * 当客户端连接时触发
     * 如果业务不需此回调可以删除onConnect
     * 
     * @param int $client_id 连接id
     */
    public static function onConnect($client_id)
    {
        Gateway::setSession($client_id, array('name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)));

        $_SESSION =
        //推送给当前client_id
        Gateway::sendToClient($client_id, json_encode(array(
            'type'      => 'init',
            'client_id' => $client_id
        )));

        //推送给所有人
        Gateway::sendToAll(json_encode([
            'type'      => 'login',
            'client_id' => $client_id
        ]));

        //tcp协议
        // 向当前client_id发送数据 
//        Gateway::sendToClient($client_id, "Hello $client_id\r\n");
//        // 向所有人发送
//        Gateway::sendToAll("$client_id login\r\n");
    }
    
   /**
    * 当客户端发来消息时触发
    * @param int $client_id 连接id
    * @param mixed $message 具体消息
    */
   public static function onMessage($client_id, $message)
   {
        // 向所有人发送 
        Gateway::sendToAll("$client_id said $message\r\n");
   }
   
   /**
    * 当用户断开连接时触发
    * @param int $client_id 连接id
    */
   public static function onClose($client_id)
   {
       //websocket协议
       //推送给所有人
       Gateway::sendToAll(json_encode([
           'type'      => 'logout',
           'client_id' => $client_id
       ]));

//       // 向所有人发送
//       GateWay::sendToAll("$client_id logout\r\n");
   }

}
3.2 按照下图启动GatewayWorker服务

下图为window系统启动成功界面 

4. 在tp6框架中去使用GatewayClient

     创建一个控制器,引入GatewayClient类进行使用就可以了。

<?php
namespace app\index\controller;
// GatewayClient 3.0.0版本开始要使用命名空间
use app\api\controller\BaseApiController;
use GatewayClient\Gateway;

class ImController extends BaseApiController
{
    public function login(){
        $client_id = request()->param('client_id');
        if(empty($client_id)){
            return $this->fail('client_id不能为空。');
        }
        Gateway::setSession($client_id, ['uid'=>rand(888,999),'name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)]);

        return $this->success("登录成功",['uid'=>rand(888,999),'name'=>"张三".rand(0,9), 'sex'=>"男".rand(0,9)]);
    }

    /**
     * @return \think\response\Json
     * @notes 当前在线人数
     * @author ssx
     */
    public function getAllClientIdCount(){
        $count = Gateway::getAllClientIdCount();
        return $this->success("当前在线{$count}人");
    }

    /**
     * @return \think\response\Json
     * @notes 获取当前所有在线client_id信息
     * @author ssx
     */
    public function getAllClientSessions(){
        $list = Gateway::getAllClientSessions();
        return $this->success("当前所有在线client_id信息",$list);
    }

    /**
     * @return \think\response\Json
     * @notes 将client_id加入某个组,以便通过Gateway::sendToGroup发送数据
     * @author ssx
     */
    public function joinGroup(){
        $client_id = request()->param('client_id');
        if(empty($client_id)){
            return $this->fail('client_id不能为空。');
        }
        // 加入某个群组(可调用多次加入多个群组)
        // 加入某个群组(可调用多次加入多个群组)
        $group = [1,2,3,4,5];
        $group = array_rand($group);
        Gateway::joinGroup($client_id, $group);
        return $this->success("加入组成功,组id:".$group,Gateway::getClientSessionsByGroup($group));
    }

    /**
     * @return \think\response\Json
     * @notes 获取全局所有在线组及成员
     * @author ssx
     */
    public function getAllGroupInfo(){
        $list = Gateway::getAllGroupIdList();
        foreach ($list as  $key => &$value) {
            //根据组id获取该组下所有client_id
            $client_id = Gateway::getClientIdListByGroup($value);
            $list[$key] = array_values($client_id);
        }
        $str = json_encode($list);
        return $this->success("当前在线组:{$str}",$list);
    }

    /**
     * @return \think\response\Json|void
     * @notes 给指定client_id发送消息
     * @author ssx
     */
    public function send(){
        $group = [1,2,3,4,5];
        var_dump(array_rand($group));die;

        $client_id = request()->param('client_id');
        if(empty($client_id)){
            return $this->fail('client_id不能为空。');
        }
        $message = "你好aaaHello".$client_id;
        // 如果不在线就先存起来
        if(!Gateway::isOnline($client_id))
        {
            // 保存未读消息
            echo '用户{$client_id}不在线';
        } else {
            // 在线就转发消息给对应的客户端
            Gateway::sendToClient($client_id,json_encode([
                'type'=>'message',
                'msg'=>$message,
            ]));
        }
        return $this->success("向{$client_id}发送消息:{$message}。");
    }
}

5.前端简单demo 

<!DOCTYPE html>
<html lang="en">
<head>
    <meta charset="UTF-8">
    <title>ceshi</title>
</head>
<body>
<p id="num" style="color:rosybrown"></p>
<p id="num_info" style="color:rosybrown"></p>
<p id="group" style="color:royalblue"></p>
<div id="aaa"></div>
</body>
</html>

<script !src="">
    // 获取id为'aaa'的元素
    var content = document.getElementById('aaa');
    var num = document.getElementById('num');
    var num_info = document.getElementById('num_info');
    var group = document.getElementById('group');
    /**
     * 与GatewayWorker建立websocket连接,域名和端口改为你实际的域名端口,
     * 其中端口为Gateway端口,即start_gateway.php指定的端口。
     * start_gateway.php 中需要指定websocket协议,像这样
     * $gateway = new Gateway(websocket://0.0.0.0:7272);
     */
    ws = new WebSocket("ws://127.0.0.1:8282");
    // 服务端主动推送消息时会触发这里的onmessage
    ws.onmessage = function(e){
        console.log(e)
        // 假设WebSocket服务器使用了UTF-8编码
        console.log(e.data)
        // json数据转换成js对象
        var data = eval("("+e.data+")");
        console.log(data)

        var type = data.type || '';
        //登录
        fetchData('http://im.api/index/im/login?client_id='+ data.client_id, function (data) {});
        //查询在线人数
        fetchData('http://im.api/index/im/getALLClientIdCount', function (data) {
            num.innerHTML = data.msg;
        });
        //获取当前所有在线client_id信息
        fetchData('http://im.api/index/im/getAllClientSessions', function (data) {
            num_info.innerHTML = "在线client_id的信息:" + JSON.stringify(data.data);
        });
        //查询在线组及其组下成员
        fetchData('http://im.api/index/im/getAllGroupInfo', function (data) {
            group.innerHTML = data.msg;
        });
        switch(type){
            // Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
            case 'init':
                // 使用innerHTML设置HTML内容
                content.innerHTML += "登录成功,你的client_id:" + data.client_id + "<br>";
                //加入一个群组
                fetchData('http://im.api/index/im/joinGroup?client_id='+ data.client_id , function (data) {
                    group.innerHTML = data.msg;
                });
                break;
            case 'login':
                // 使用innerHTML设置HTML内容
                content.innerHTML += "用户登录,client_id:" + data.client_id + "<br>";
                break;
            case 'logout':
                // 使用innerHTML设置HTML内容
                content.innerHTML += "用户下线,client_id:" + data.client_id + "<br>";
                break;
            default :
                // 使用innerHTML设置HTML内容
                content.innerHTML += "收到推送消息:" + data.msg + "<br>";
        }
    };

    function fetchData(url,callback) {
        var xhr = new XMLHttpRequest();
        xhr.onreadystatechange = function () {
            if (xhr.readyState == 4 && xhr.status == 200) {
                //json转对象
                var data = JSON.parse(xhr.responseText);
                console.log(data);
                callback(data);
            }
        };
        xhr.open('GET', url, true);
        xhr.send();
    }

</script>

效果实现如下图

 

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值