PHP Yii2 框架 使用workerman实现websocket通讯

记录workerman 实现 websocket通讯 貼上workerman链接:https://www.workerman.net/workerman,这里不做太多解释了,直接进入主题
首先需要在框架里安装workerman,我这里使用 git 安装的:git clone https://github.com/walkor/Workerman
在这里插入图片描述
我是放在了 项目主目录下
2. 搭建socket服务器
test.php如下

<?php
use Workerman\Lib\Timer;
use Workerman\Worker;
require_once './Workerman/Autoloader.php';

// 心跳间隔55秒
define('HEARTBEAT_TIME', 55);

// 初始化一个worker容器,监听1415端口
$worker = new Worker('websocket://0.0.0.0:1415');

/*
 * 注意这里进程数必须设置为1,否则会报端口占用错误
 * (php 7可以设置进程数大于1,前提是$inner_text_worker->reusePort=true)
 */
$worker->count = 1;
// worker进程启动后创建一个text Worker以便打开一个内部通讯端口
$worker->onWorkerStart = function($worker)
{

    Timer::add(1, function()use($worker){
        $time_now = time();
        foreach($worker->connections as $connection) {
            // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
            if (empty($connection->lastMessageTime)) {
                $connection->lastMessageTime = $time_now;
                continue;
            }
            // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
            if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                $connection->close();
            }
        }
    });

    // 开启一个内部端口,方便内部系统推送数据,Text协议格式 文本+换行符
    $inner_text_worker = new Worker('text://127.0.0.1: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');
    };
    // ## 执行监听 ##
    $inner_text_worker->listen();
};
// 新增加一个属性,用来保存uid到connection的映射
$worker->uidConnections = array();
// 当有客户端发来消息时执行的回调函数
$worker->onMessage = function($connection, $data)
{
    global $worker;

    // 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间
    $connection->lastMessageTime = time();

    // 判断当前客户端是否已经验证,既是否设置了uid
    if(!isset($connection->uid))
    {
        // 没验证的话把第一个包当做uid(这里为了方便演示,没做真正的验证)
        $connection->uid = $data;
        /* 保存uid到connection的映射,这样可以方便的通过uid查找connection,
         * 实现针对特定uid推送数据
         */
        $worker->uidConnections[$connection->uid] = $connection;
        return;
    }
};

// 当有客户端连接断开时
$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 test.php start
在这里插入图片描述
运行状态
客户端:

<script type="text/javascript">
        var wsClient=null;
        var wsProtocol = 'https:' == document.location.protocol ? 'wss://' : 'ws://';
        //let ip = window.location.host; 
        let ws = new WebSocket(wsProtocol+'127.0.0.1:1415');//自己服务器ip或域名
        window.setInterval(ws.onopen = function(){
                var uid = 16;
                ws.send(uid);
                console.log("链接成功,发送id:"+uid);
        }, 40000) //这里是每40s重新建立下连接防止si掉
        ws.onmessage = function(e){
            var message_info = JSON.parse(e.data)
            console.log(e.data);
            if(message_info.popup_tips == 1){
                $('#MessageBox').show();
            }
            if(message_info.popup_voice == 1){
                let yinpin = "/Cuetone/Cuetone-shenhao.mp3";
                let html = '<audio autoplay="autoplay">\n' +
                    '<source src="'+yinpin+'" />\n' +
                    '</audio>'
                $('.yinyue').html(html);
            }
        };
    </script>

测试 是否连接成功
在这里插入图片描述
最后开始推送消息
这里选择使用队列来执行 像客户端发送消息

<?php
/**
 *
 * @author li
 * 审核通知
 */

namespace common\component\jobs;

use Yii;
use yii\base\BaseObject;
use yii\queue\RetryableJobInterface;


class NeedSellerCheckAccount extends BaseObject implements RetryableJobInterface {
    public $sellerid;
    public $popup_tips;
    public $popup_voice;

    public function execute($queue)
    {
        try {
                echo '用户:' .$this->sellerid . ' 弹窗:' . $this->popup_tips ."声音:".$this->popup_voice."\n";
                $client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
                print_r($client);
                // 推送的数据,包含uid字段,表示是给这个uid推送
                $data = array('uid' => $this->sellerid, 'popup_tips' => $this->popup_tips,'popup_voice' =>$this->popup_voice);
                    // 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
                fwrite($client, json_encode($data) . "\n");
                    // 读取推送结果
                echo "推送消息\n";
                echo fread($client, 8192);
                return true;
        } catch(\Exception $e) {
            echo "队列执行异常:$this->sellerid " . $e->getMessage()."行" .$e->getLine() .' 时间:' .time();
            throwException($e);
            return false;
        }
    }


    public function getTtr()
    {
        return 300;
    }

    public function canRetry($attempt, $error)
    {
        return ($attempt < 3) && ($error instanceof \Exception);
    }

}

<?php

/***
 * 零时性跑的脚本
 */

namespace console\controllers;

use common\component\jobs\NeedSellerCheckAccount;

class TempController extends Controller
{
    public function actionPush()
    {
        while (true){
            $seller_id = 16;
            $popup_tips = 1;
            $popup_voice = 1;
            Yii::$app->push_seller->push(new NeedSellerCheckAccount([
                'sellerid' => $seller_id,
                'popup_tips' => $popup_tips, //弹窗
                'popup_voice' => $popup_voice, // 声音
            ]));
            sleep(15);
        }

    }
}

执行脚本
在这里插入图片描述
监听队列执行情况
在这里插入图片描述
客户端查看
在这里插入图片描述
连接推送成功
这里有一些要注意,就是注意到测试服务器或者正式服务器时,客服端发送请求地址要是自己的域名,然后别忘了开端口号,根据自己需求使用守护进程,虽然它自带守护进程,最好是一起,方便管理

  • 1
    点赞
  • 5
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值