ThinkPHP部署Workerman的成功使用示例

寻技术 PHP编程 / ThinkPHP 2023年11月21日 99

本文介绍thinkphp中关于composer集成workerman的方法,并解决了安装过程 中遇到的错误,实现了和woerkman进行握手和通信的demo。用户可以在此基础上按自己的逻辑实现一个聊天系统或者客服系统。

一、安装扩展包   composer require topthink/think-worker

直接执行:composer require topthink/think-worker=1.0.*     即可成功

二、新建 server.php

#!/usr/bin/env php
<?php
define('APP_PATH', __DIR__ . '/application/');
define('BIND_MODULE','push/Worker');
// 加载框架引导文件
require __DIR__ . '/thinkphp/start.php';

三、新建Worker.php    

php think make:controller push/Worker

即可,将里面的内容替换如下所示:

<?php
namespace app\push\controller;
use think\Controller;
use think\Request;
use think\worker\Server;
use think\Cache;
class Worker extends Server
{
    protected $socket = 'websocket://0.0.0.0:2346';
    protected $processes = 1;
    protected $uidConnections = array();
    static $count  = 0;
    /**
     * 收到信息
     * @param $connection
     * @param $data
     */
    public function onMessage($connection, $data)
    {
        $retdata=json_decode($data,true);
        $uid=$retdata['id'];
        $message=$retdata['msg'];
        if(isset($this->uidConnections[$uid]))
        {
            $connection = $this->uidConnections[$uid];
            $connection->send($message);
            // return true;
        }
        $connection->send('我收到你的信息了333='.$retdata['msg']);
    }
    /**
     * 当连接建立时触发的回调函数
     * @param $connection
     */
    public function onConnect($connection)
    {
        $this->uidConnections[$connection->id] = $connection;
        $connection->send('你连接了我='.$connection->id);
    }
    // 针对uid推送数据
    public function sendMessageByUid($uid, $message)
    {
        if(isset($this->uidConnections[$uid]))
        {
            $connection = $this->uidConnections[$uid];
            $connection->send($message);
            return true;
        }
        return false;
    }
    /**
     * 当连接断开时触发的回调函数
     * @param $connection
     */
    public function onClose($connection)
    {
    }
    /**
     * 当客户端的连接上发生错误时触发
     * @param $connection
     * @param $code
     * @param $msg
     */
    public function onError($connection, $code, $msg)
    {
        echo "error $code $msg\n";
    }
    /**
     * 每个进程启动
     * @param $worker
     */
    public function onWorkerStart($worker)
    {
    }
}

四、执行 php server.php start

遇到禁用函数就去对应的PHP里面把禁用函数删除 (此命令可以放到Supervisor的守护进程里面去),并且查看端口是否运行,宝塔里面也要放行对应的端口 2346

测试远程连接: telnet localhost 2346         localhost改成外网ip即可,这个走通了,前端就能直接连接了

linux 退出Telnet命令    先输入命令:CTRL+]然后再输入命令:quit

五、前端代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Title</title>
</head>
<body>
<script>
    ws = new WebSocket("ws://118.**.***.207:2346");
    ws.onopen = function() {
        console.log("连接成功");
        ws.send('tom');
        console.log("给服务端发送一个字符串:tom");
    };
    ws.onmessage = function(e) {
        console.log("收到服务端的消息:" + e.data);
    };
</script>
</body>
</html>

六、前端开两个端口即可进行相互通讯:

ws.send('{"id":"2","msg":"21111111111110"}');

重点在这:因为在这里需要用到服务端给客户端推送,用到了text服务

WorkerMan中php后端及时推送消息给客户端

原理:

1、建立一个websocket Worker,用来维持客户端长连接

2、websocket Worker内部建立一个text Worker

3、websocket Worker 与 text Worker是同一个进程,可以方便的共享客户端连接

4、某个独立的php后台系统通过text协议与text Worker通讯

5、text Worker操作websocket连接完成数据推送

代码及步骤

//push.php
<?php
use Workerman\Worker;
require_once './vendor/workerman/workerman/Autoloader.php';
// 初始化一个worker容器,监听1234端口
$worker = new Worker('websocket://0.0.0.0:1234');//
/*
 * 注意这里进程数必须设置为1,否则会报端口占用错误
 * (php 7可以设置进程数大于1,前提是$inner_text_worker->reusePort=true)
 */
$worker->count = 1;
// worker进程启动后创建一个text Worker以便打开一个内部通讯端口
$worker->onWorkerStart = function($worker)
{
    // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
    $inner_text_worker = new Worker('text://0.0.0.0:5678');
    $inner_text_worker->onMessage = function($connection, $buffer)
    {
        // $data数组格式,里面有uid,表示向那个uid的页面推送数据
        $data = json_decode($buffer, true);
        $uid = $data['uid'];
        // 通过workerman,向uid的页面推送数据
        $ret = sendMessageByUid($uid, $buffer);
        // 返回推送结果
        $connection->send($ret ? 'ok' : 'fail');
    };
    // $connection->send('你好,你连接我了');
    // ## 执行监听 ##
    $inner_text_worker->listen();
};
// 新增加一个属性,用来保存uid到connection的映射
$worker->uidConnections = array();
// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function($connection, $data)
{
    $data=json_decode($data,true);
    $connection->send('99897');
    global $worker;
    // 判断当前客户端是否已经验证,既是否设置了uid
    if(!isset($connection->uid))
    {
        // 没验证的话把第一个包当做uid(这里为了方便演示,没做真正的验证)
        $connection->uid = $data['id'];
        /* 保存uid到connection的映射,这样可以方便的通过uid查找connection,
         * 实现针对特定uid推送数据
         */
        $worker->uidConnections[$connection->uid] = $connection;
        $connection->send('9980'.$data['msg']);
        return;
    }else{
         $connection->send('998123');
    }
};
// 当有客户端连接断开时
$worker->onClose = function($connection)
{
    global $worker;
    if(isset($connection->uid))
    {
        // 连接断开时删除映射
        unset($worker->uidConnections[$connection->uid]);
    }
};
// 向所有验证的用户推送数据
function broadcast($message)
{
    global $worker;
    foreach($worker->uidConnections as $connection)
    {
        $connection->send($message);
    }
}
// 针对uid推送数据
function sendMessageByUid($uid, $message)
{
    global $worker;
    if(isset($worker->uidConnections[$uid]))
    {
        $connection = $worker->uidConnections[$uid];
        $connection->send($message);
        return true;
    }
    return false;
}

运行所有的worker

Worker::runAll();
启动后端服务 php push.php start -d

前端接收推送的js代码

<!DOCTYPE html>
<html lang="en">
<head>
    <meta http-equiv="Content-Type" content="text/html; charset=utf-8" />
    <title>Title</title>
</head>
<body>
<script>
    var ws = new WebSocket('ws://118.**.**.207:1234');
    ws.onopen = function(){
        var uid = 'uid1';
        ws.send(uid);
        console.log("给服务端发送一个字符串:"+uid);
    };
    ws.onmessage = function(e){
        // alert(e.data);
        console.log("收到服务端的消息:" + e.data);
    };
</script>
</body>
</html>

后端推送消息的代码

// 建立socket连接到内部推送端口
$client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
// 推送的数据,包含uid字段,表示是给这个uid推送
$data = array('uid'=>'uid1', 'percent'=>'88%');
// 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
fwrite($client, json_encode($data)."\n");
// 读取推送结果
echo fread($client, 8192);

后端推送消息的代码和push.php监听同一个端口

push.php和前端监听同一个websocket端口

通过后端推送消息的代码向push.php推送数据,

push.php接受到数据后通过处理 利用websocket往前端推送数据

关闭

用微信“扫一扫”