讲师中心 微信公众号
AI工具推荐 视频效率加速

ThinkPHP5.1框架与Workerman之GatewayWorker框架结合案例

夜芳君_8672

夜芳君_8672

发布时间:2019-04-10 11:26:13

|

9230人浏览过

|

来源于Tinywan

转载

gatewayworker是基于workerman开发的一个可分布式部署的tcp长连接框架,专门用于快速开发tcp长连接应用,例如app推送服务端、即时im服务端、游戏服务端、物联网、智能家居等等

文档地址:http://www.workerman.net/gatewaydoc/

fe56ce704dd4dd8c894d200ed9f06e3.png

一、测试官方DEMO(Windows 版本)

1、下载demo(在下方评论中自取)

立即学习“PHP免费学习笔记(深入)”;

2、解压到任意位置,我这里为:

D:\phpStudy\PHPTutorial\WWW\GatewayWorker

3、进入GatewayWorker目录

4、双击start_for_win.bat启动。(如果出现错误请参考这里设置php环境变量),效果如下

771a0d5dbfe83ead0b87eacc3d0876a.png

5、命令行窗口运行 telnet 127.0.0.1 8282,输入任意字符即可聊天(非本机测试请将127.0.0.1替换成实际ip)。

b91ab1736cf4533bee39c25e96280ae.png

PS:以上表示TCP连接测试成功

二、修改测试websocket

1、需要修改 start_gateway.php 指定websocket协议,像这样

$gateway = new Gateway(websocket://0.0.0.0:7272);

2、重新启动 start_for_win.bat

3、测试js

0da29686a0423f92614631a608fb060.png

小结:只需要改动一个文件( start_gateway.php)的协议和端口即可,别的不需用改动。

三、与ThinkPHP5.1框架结合

(一)服务端主动推送消息到客户端

原则:

1、TP5.1框架项目与GatewayWorker独立部署互不干扰

2、所有的业务逻辑都由网站(websocket连接的)页面以post/get请求到TP5.1框架的控制器中完成

3、GatewayWorker不接受客户端发来的数据,即GatewayWorker不处理任何业务逻辑,GatewayWorker仅仅当做一个单向的推送通道

4、仅当TP5.1框架需要向浏览器主动推送数据时才在TP5.1框架中调用Gateway的API(GatewayClient)完成推送

具体实现步骤

1、网站页面建立与GatewayWorker的websocket连接

ws = new WebSocket("ws://127.0.0.1:7272");

 2、GatewayWorker发现有页面发起连接时,将对应连接的client_id发给网站页面

Event.php 内容

public static function onConnect($client_id)
{
    $resData = [
        'type' => 'init',
        'client_id' => $client_id,
        'msg' => 'connect is success' // 初始化房间信息
    ];
    Gateway::sendToClient($client_id, json_encode($resData));
}

index.html 内容

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>GatewayWorker的websocket连接</title>
</head>
<body>
<h1>GatewayWorker的websocket连接</h1>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script type="text/javascript">
    ws = new WebSocket("ws://127.0.0.1:7272");
    // 服务端主动推送消息时会触发这里的onmessage
    ws.onmessage = function(e){
        // json数据转换成js对象
        var data = JSON.parse(e.data);
        console.log(data);
        var type = data.type || '';
        switch(type){
            // Events.php中返回的init类型的消息,将client_id发给后台进行uid绑定
            case 'init':
                // 利用jquery发起ajax请求,将client_id发给后端进行uid绑定
                $.post(
                    "{:url('index/chat_room/bind')}",
                    {client_id: data.client_id},
                    function(data)
                    {
                        console.log(data);
                    },
                    'json'
                );
                break;
            case  'say':
                console.log('TP5 msg'+e.data);
                break;
            // 当mvc框架调用GatewayClient发消息时直接alert出来
            default :
                alert(e.data);
        }
    };
</script>
</body>
</html>

3、网站页面收到client_id后触发一个ajax请求(index/chat_room/bind)将client_id发到TP5.0后端,bind方法

/*
 * 用户登录后初始化以及绑定client_id
 */
public function bind()
{
    // 设置GatewayWorker服务的Register服务ip和端口,请根据实际情况改成实际值
    Gateway::$registerAddress = '127.0.0.1:1238';
    $uid = $this->userId;
    $group_id = $this->groupId;
    $client_id = request()->param('client_id');
    // client_id与uid绑定
    Gateway::bindUid($client_id, $uid);
    // 加入某个群组(可调用多次加入多个群组)
    Gateway::joinGroup($client_id, $group_id);
}

4、后端收到client_id后利用GatewayClient调用Gateway::bindUid($client_id, $uid)将client_id与当前uid(用户id或者客户端唯一标识)绑定。如果有群组、群发功能,也可以利用Gateway::joinGroup($client_id, $group_id)将client_id加入到对应分组

连接成功后返回值

18e8f785b8c96159adec3f449c70eff.png

PS:以上返回值为 GatewayWorker服务 连接成功后返回的json数据

5、页面发起的所有请求都直接post/get到mvc框架统一处理,包括发送消息

通过sendMessage发送消息(服务端主动推送消息到客户端)

// mvc后端发消息 利用GatewayClient发送 Events.php
public function sendMessage()
{
    // stream_socket_client(): unable to connect to tcp://127.0.0.1:1236
    $uid = $this->userId;
    $group = $this->groupId;
    $message = json_encode([
      'type'=>'say',
      'msg'=>'Hello ThinkPHP5'
    ]);
    // 设置GatewayWorker服务的Register服务ip和端口,请根据实际情况改成实际值
    Gateway::$registerAddress = '127.0.0.1:1238';
    // 向任意uid的网站页面发送数据
    Gateway::sendToUid($uid, $message);
    // 向任意群组的网站页面发送数据,如果开启,则会向页面发送两条一样的消息
    //Gateway::sendToGroup($group, $message);
}

6、mvc框架处理业务过程中需要向某个uid或者某个群组发送数据时,直接调用GatewayClient的接口Gateway::sendToUid Gateway::sendToGroup 等发送即可

通过浏览器访问sendMessage操作,测试结果

a72f4ce2d15a6f76b516da053a34d8e.png

PS:以上的消息是TP5.0 通过 GatewayClient\Gateway 发送写消息,和GatewayWorker服务没有直接关系

以上为 服务端主动推送消息到客户端

注意区分:

1、服务端主动推送消息到客户端

2、客户端推送消息到客户端

(二)客户端推送消息到客户端

修改客户端到客户端的消息发送和接受,下面修改 GatewayWorker 的 Events.php(开发者只需要关注这个文件)

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)
{
    // 服务端console输出
    //echo "msg : $message \r\n";
 
    // 解析数据
    $resData = json_decode($message, true);
    $type = $resData['type'];
    $roomId = $resData['roomId'];
    $userId = $resData['userId']; // 未登录,则传递一个随机
    $userName = $resData['userName']; // 未登录,则传递一个随机
    $content = isset($resData['content']) ? $resData['content'] : 'default content';
     
    //将时间全部置为服务器时间
    $serverTime = date('Y-m-d H:i:s', time());
 
    switch ($type) {
        case 'join':  // 用户进入直播间
            //将客户端加入到某一直播间
            Gateway::joinGroup($client_id, $roomId);
            $resData = [
                'type' => 'join',
                'roomId' => $roomId,
                'userName' => $userName,
                'msg' => "enters the Room", // 发送给客户端的消息,而不是聊天发送的内容
                'joinTime' => $serverTime // 加入时间                   
            ];
 
            // 广播给直播间内所有人,谁?什么时候?加入了那个房间?
            Gateway::sendToGroup($roomId, json_encode($resData));
            break;
        case 'say':  // 用户发表评论
            $resData = [
                'type' => 'say',
                'roomId' => $roomId,
                'userName' => $userName,
                'content' => $content,
                'commentTime' => $serverTime // 发表评论时间
            ];
            // 广播给直播间内所有人
            Gateway::sendToGroup($roomId, json_encode($resData));
            break;
        case 'pong':
            break; // 接收心跳
        default:
            //Gateway::sendToAll($client_id,$json_encode($resData));
            break;
    }
}

index.html 聊天室页面

<!DOCTYPE html>
<html>
<head>
    <meta charset="UTF-8">
    <title>GatewayWorker的websocket连接</title>
</head>
<body>
<h1>GatewayWorker的websocket连接</h1>
<div>
    websocket send content:<input type="text" style="height: 50px; width: 100%;" name="data" id="data">
    <p></p>
    <button id="submit" onclick="sub()">send info</button>
    <p></p>
    <div id="output"></div>
</div>
<script src="https://cdn.bootcss.com/jquery/3.3.1/jquery.min.js"></script>
<script src="https://cdn.bootcss.com/reconnecting-websocket/1.0.0/reconnecting-websocket.min.js"></script>
<script language="javascript" type="text/javascript">
    var wsUri = "ws://notes.env:7272/";
    var outputContent;
    var roomId = 'L06777';
    var userId = 4840043;
    var userName = 'Tinywan' + Math.random();
 
    // 把当新链接的客户端加入到当前直播间,消息类型:{"type":"join","roomId":"1002","userId":"88","userName":"userName"}
    var joinContent = {
        "type": "join",
        "roomId": roomId,
        "userId": userId,
        "userName": userName
    };
 
    // 初始化页面操作
    function init() {
        outputContent = document.getElementById("output");
        initWebSocket();
    }
 
    function initWebSocket() {
        websocket = new ReconnectingWebSocket(wsUri);
        websocket.onopen = function (evt) {
            onOpen(evt)
        };
        websocket.onclose = function (evt) {
            onClose(evt)
        };
        websocket.onmessage = function (evt) {
            onMessage(evt)
        };
        websocket.onerror = function (evt) {
            onError(evt)
        };
    }
 
    function onOpen(evt) {
        console.log("CONNECTED");
    }
 
    // 接收数据
    function onMessage(evt) {
        var data = eval("(" + evt.data + ")");
        var type = data.type || '';
        switch (type) {
            case 'init':
                // 把当新链接的客户端加入到当前直播间
                console.log('-------init--------' + data);
                websocket.send(JSON.stringify(joinContent));
                writeToScreen('<span style="color: blue;">RESPONSE: ' + evt.data + '</span>');
                break;
            case 'join':
                console.log('-------join--------' + data);
                writeToScreen(
                    '<span style="color: blue;"> ' + ' 新用户: ' + '</span>' +
                    '<span style="color: red;"> ' + data.userName + '</span>' +
                    '<span style="color: green;"> ' + data.joinTime + '</span>' +
                    '<span style="color: black;"> ' + data.msg + '</span>'
                );
                break;
            case 'say':
                console.log('say======' + data);
                writeToScreen(
                    '<span style="color: blue;"> ' + ' Chat: ' + '</span>' +
                    '<span style="color: red;"> ' + data.userName + '</span>' +
                    '<span style="color: #D2691E;"> ' + data.commentTime + '</span>' +
                    '<span style="color: black;"> ' + data.content + '</span>'
                );
                break;
            default :
                console.log(data);
                break;
        }
    }
 
    function onError(evt) {
        console.log('<span style="color: red;">ERROR:</span> ' + evt.data);
    }
 
    function onClose(evt) {
        console.log("DISCONNECTED");
    }
 
    function writeToScreen(message) {
        var pre = document.createElement("p");
        pre.style.wordWrap = "break-word";
        pre.innerHTML = message;
        outputContent.appendChild(pre);
    }
 
    function sub() {
        var text = document.getElementById('data').value;
        // {"type":"say",,"msg":"Welcome 111111111111Live Room"}
        var sayContent = {
            "type": "say",
            "roomId": roomId,
            "userId": userId,
            "userName": userName,
            "content": text
        };
        websocket.send(JSON.stringify(sayContent));
    }
    window.addEventListener("load", init, false);
</script>
</body>
</html> 

重启开启服务

测试结果

0bc2df95a7a7de5201b4063489034c2.png

扩展:

可以把消息存储的Redis中,通过Redis统计直播间的PV

$redis = new \Redis;
$redis->connect('127.0.0.1',6379);
$key = "PV:ROOM:".$roomId;
$field = "ROOM_TOTAL_PV";
// 进入房间的人数增长,自增 ,增加PV统计
$redis->hIncrBy($key,$field,1);

相关推荐:《PHP教程》

相关文章

PHP速学教程(入门到精通)
PHP速学教程(入门到精通)

PHP怎么学习?PHP怎么入门?PHP在哪学?PHP怎么学才快?不用担心,这里为大家提供了PHP速学教程(入门到精通),有需要的小伙伴保存下载就能学习啦!

下载

相关标签:

本站声明:本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系admin@php.cn

热门AI工具

更多
UP简历
UP简历 Hot

一款AI办公效率工具,主要用于基于AI技术的免费在线简历制作工具,适合需要提升相关任务效率的用户。

墨刀AI
墨刀AI Hot

一款AI图像与设计工具,主要用于产品经理的专属智能体,适合需要提升相关任务效率的用户。

立刻MV
立刻MV Hot

立刻MV是一款AI文本写作工具,AI 音乐视频(MV)创作工具。

切问学术

切问学术是一款AI论文写作工具,复旦大学NLP团队推出的AI学术智能体。

火山引擎

火山引擎是一款面向企业的云计算与AI服务平台。

豆包大模型

豆包大模型是一款由字节跳动推出的企业级大语言模型服务平台。

DeepSeek

DeepSeek是一款面向对话、写作、编程和推理场景的AI大模型工具。

WorkBuddy

一款AI办公效率工具,主要用于腾讯云推出的AI原生桌面智能体工作台,适合需要提升相关任务效率的用户。

蛙蛙写作

一款AI论文写作工具,主要用于超级AI智能写作助手,适合需要提升相关任务效率的用户。

相关专题

更多
什么是分布式
什么是分布式

分布式是一种计算和数据处理的方式,将计算任务或数据分散到多个计算机或节点中进行处理。本专题为大家提供分布式相关的文章、下载、课程内容,供大家免费下载体验。

1933

2023.08.11

分布式和微服务的区别
分布式和微服务的区别

分布式和微服务的区别在定义和概念、设计思想、粒度和复杂性、服务边界和自治性、技术栈和部署方式等。本专题为大家提供分布式和微服务相关的文章、下载、课程内容,供大家免费下载体验。

2754

2023.10.07

504 gateway timeout怎么解决
504 gateway timeout怎么解决

504 gateway timeout的解决办法:1、检查服务器负载;2、优化查询和代码;3、增加超时限制;4、检查代理服务器;5、检查网络连接;6、使用负载均衡;7、监控和日志;8、故障排除;9、增加缓存;10、分析请求。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

754

2023.11.27

default gateway怎么配置
default gateway怎么配置

配置default gateway的步骤:1、了解网络环境;2、获取路由器IP地址;3、登录路由器管理界面;4、找到并配置WAN口设置;5、配置默认网关;6、保存设置并退出;7、检查网络连接是否正常。本专题为大家提供相关的文章、下载、课程内容,供大家免费下载体验。

357

2023.12.07

json数据格式
json数据格式

JSON是一种轻量级的数据交换格式。本专题为大家带来json数据格式相关文章,帮助大家解决问题。

2015

2023.08.07

json是什么
json是什么

JSON是一种轻量级的数据交换格式,具有简洁、易读、跨平台和语言的特点,JSON数据是通过键值对的方式进行组织,其中键是字符串,值可以是字符串、数值、布尔值、数组、对象或者null,在Web开发、数据交换和配置文件等方面得到广泛应用。本专题为大家提供json相关的文章、下载、课程内容,供大家免费下载体验。

2902

2023.08.23

jquery怎么操作json
jquery怎么操作json

操作的方法有:1、“$.parseJSON(jsonString)”2、“$.getJSON(url, data, success)”;3、“$.each(obj, callback)”;4、“$.ajax()”。更多jquery怎么操作json的详细内容,可以访问本专题下面的文章。

996

2023.10.13

go语言处理json数据方法
go语言处理json数据方法

本专题整合了go语言中处理json数据方法,阅读专题下面的文章了解更多详细内容。

3259

2025.09.10

LLVM自定义Pass怎么写
LLVM自定义Pass怎么写

本专题聚焦LLVM自定义Pass开发,整理Pass类结构、run()方法、PreservedAnalyses、CMake构建、插件注册、-load-pass-plugin加载和测试用例编写流程。

80

2026.09.30

热门下载

更多
网站特效
/
网站源码
/
网站素材
/
前端模板

精品课程

更多
相关推荐
/
热门推荐
/
最新课程
ThinkPHP5.1完全开发手册
ThinkPHP5.1完全开发手册

共0课时 | 0人学习

Thinkphp5.1开发实战
Thinkphp5.1开发实战

共78课时 | 9.2万人学习

ThinkPHP5.1企业站点快速开发
ThinkPHP5.1企业站点快速开发

共30课时 | 6.4万人学习

关于我们 免责申明 举报中心 意见反馈 讲师合作 广告合作 最新更新
php中文网:公益在线php培训,帮助PHP学习者快速成长!
关注服务号
PHP中文网订阅号
每天精选资源文章推送

Copyright 2014-2026 https://www.php.cn/ All Rights Reserved | php.cn