laravel与workerman结合

news2024/11/23 1:20:35

安装workerman

composer require workerman/workerman

生成命令

php artisan make:command Workerman
<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Workerman\Worker;

class Workerman extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'Workerman {action} {--daemonize}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        global $argv;//定义全局变量
        $arg = $this->argument('action');
        $argv[1] = $arg;
        $argv[2] = $this->option('daemonize') ? '-d' : '';//该参数是以daemon(守护进程)方式启动

        global $text_worker;
        // 创建一个Worker监听2347端口,使用websocket协议通讯
        $text_worker = new Worker("websocket://0.0.0.0:2347");
        $text_worker->uidConnections = array();//在线用户连接对象
        $text_worker->uidInfo = array();//在线用户的用户信息
        // 启动1个进程对外提供服务,这里设置多个进程在服务端发送客户端时会有问题
        $text_worker->count = 1;
        //当启动workerman的时候 触发此方法
        $text_worker->onWorkerStart =function(){
            
        };
        //当浏览器连接的时候触发此函数
        $text_worker->onConnect = function($connection){

        };
        //向用户发送信息的时候触发
        //$connection 当前连接的人的信息 $data 发送的数据
        $text_worker->onMessage = function($connection,$data){
            
        };
        //浏览器断开链接的时候触发
        $text_worker->onClose = function($connection){};
        Worker::runAll();
    }
}

修改文件

App\Console\Kernel
这样就可以运行:php artisan Workerman start 命令

	/**
     * The Artisan commands provided by your application.
     *
     * @var array
     */
    protected $commands = [
        Commands\Workerman::class,
    ];

启动命令

# php artisan Workerman start
Workerman[artisan] start in DEBUG mode
------------------------------------------- WORKERMAN --------------------------------------------
Workerman version:4.1.6          PHP version:8.0.18           Event-Loop:\Workerman\Events\Select
-------------------------------------------- WORKERS ---------------------------------------------
proto   user            worker          listen                      processes    status           
tcp     root            none            websocket://0.0.0.0:2347    1             [OK]            
--------------------------------------------------------------------------------------------------

浏览器之间通信

后端

//$connection 当前连接的人的信息 $data 发送的数据
$text_worker->onMessage = function($connection,$data){
     $data = json_decode($data, true);
     if($data['type']=='login'){
         $this->create_uid($connection,$data);
     }
};
//创建uid方法
public function create_uid($connection,$data){
    global $text_worker;
    $connection->uid = $data['uid'];
    //保存用户的uid
    $text_worker->uidConnections["{$connection->uid}"] = $connection;
    //向自己的浏览器返回创建成功的信息
    $connection->send("用户:[{$connection->uid}] 创建成功");
}

打开两个新的页面、打开控制台选择 Console

// 第一个页面
var socket = new WebSocket("ws://127.0.0.1:2347//ws");
// 建立连接时触发 建立链接的时候,需要向workerman发送一条指令,告诉他我是谁,使用id或者用户标识作为uid,告诉workerman 例如,当前html 用户id是36
socket.onopen = function(event) {
   console.log('连接开始...');
   socket.send('{"uid":36,"type":"login"}');
}
//workerman发送消息的时候,接收并打印
socket.onmessage = function(event) {
   var msg = event.data;
   console.log(msg );
}

//第二个页面
var socket = new WebSocket("ws://127.0.0.1:2347//ws");
// 建立连接时触发 建立链接的时候,需要向workerman发送一条指令,告诉他我是谁,使用id或者用户标识作为uid,告诉workerman 例如,当前html 用户id是37
socket.onopen = function(event) {
   console.log('连接开始...');
   socket.send('{"uid":37,"type":"login"}');
}
//workerman发送消息的时候,接收并打印
socket.onmessage = function(event) {
   var msg = event.data;
   console.log(msg );
}

在这里插入图片描述


向其他用户发送信息
后端

$text_worker->onMessage = function($connection,$data){
    $data = json_decode($data, true);
    if($data['type']=='login'){
        $this->create_uid($connection,$data);
    }
    if($data['type']=='send_message'){
        $this->send_message($connection,$data);
    }
};

public function send_message($connection,$data){
    global $text_worker;
    if(isset($data['to_uid'])){
        var_dump($data['to_uid']);
        if(isset($text_worker->uidConnections["{$data['to_uid']}"])){
            $to_connection=$text_worker->uidConnections["{$data['to_uid']}"];
            $to_connection->send($data['uid'].$data['message']);
        }
    }
}

页面,在uid为37的worker下

socket.send('{"type":"send_message","to_uid":36,"uid":37,"message":"nihao"}');

在这里插入图片描述

服务器向浏览器通信

workeman 监听一个本地发送的端口,在启动的时候

//当启动workerman的时候 触发此方法
$text_worker->onWorkerStart =function(){
    //监听一个内部端口,用来接收服务器的消息,转发给浏览器
    $inner_text_worker = new Worker('text://127.0.0.1:5678');
    $inner_text_worker->onMessage = function($connection_admin, $data)
    {
        global $text_worker;
        // $data数组格式,里面有uid,表示向那个uid的页面推送数据
        $buffer = json_decode($data, true);
        var_dump($buffer);
        $to_uid = $buffer['to_uid'];
        var_dump($to_uid);
        
        // 通过workerman,向uid的页面推送数据
        $connection = $text_worker->uidConnections[$to_uid];
        $ret = $connection->send($data);
        // 返回推送结果
        $connection_admin->send($ret ? 'ok' : 'fail');
    };
    $inner_text_worker->listen();
};

Route::get('/', function () {
    $client = stream_socket_client('tcp://127.0.0.1:5678', $errno, $errmsg, 1);
    // 推送的数据,包含用户,表示是给这个用户推送
    $data = array ('uid'=>37,'group'=>'admin', 'message'=>'发送成功啦', 'to_uid'=>36); 
    // 发送数据,注意5678端口是Text协议的端口,Text协议需要在数据末尾加上换行符
    fwrite($client, json_encode($data)."\n");
    
    echo fread($client, 8192);
});

在这里插入图片描述

添加心跳的完整代码

<?php

namespace App\Console\Commands;

use Illuminate\Console\Command;
use Workerman\Worker;
use Workerman\Timer;

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

class Workerman extends Command
{
    /**
     * The name and signature of the console command.
     *
     * @var string
     */
    protected $signature = 'Workerman {action} {--daemonize}';

    /**
     * The console command description.
     *
     * @var string
     */
    protected $description = 'Command description';

    /**
     * Create a new command instance.
     *
     * @return void
     */
    public function __construct()
    {
        parent::__construct();
    }

    /**
     * Execute the console command.
     *
     * @return int
     */
    public function handle()
    {
        global $argv;//定义全局变量
        $arg = $this->argument('action');
        $argv[1] = $arg;
        $argv[2] = $this->option('daemonize') ? '-d' : '';//该参数是以daemon(守护进程)方式启动

        global $text_worker;
        // 创建一个Worker监听2345端口,使用websocket协议通讯
        $text_worker = new Worker("websocket://0.0.0.0:2347");
        $text_worker->uidConnections = array();//在线用户连接对象
        $text_worker->uidInfo = array();//在线用户的用户信息
        // 启动4个进程对外提供服务
        $text_worker->count = 1;
        //当启动workerman的时候 触发此方法
        $text_worker->onWorkerStart =function(){
            //监听一个内部端口,用来接收服务器的消息,转发给浏览器
            $inner_text_worker = new Worker('text://127.0.0.1:5678');
            $inner_text_worker->onMessage = function($connection_admin, $data)
            {
                global $text_worker;
                // $data数组格式,里面有uid,表示向那个uid的页面推送数据
                $buffer = json_decode($data, true);
                //var_dump($buffer);
                $to_uid = $buffer['to_uid'];
                //var_dump($to_uid);
                
                // 通过workerman,向uid的页面推送数据
                if(isset($text_worker->uidConnections[$to_uid])){
                    $connection = $text_worker->uidConnections[$to_uid];
                    $ret = $connection->send($data);
                } else {
                    var_dump($to_uid . ': not define');
                    $ret = false;
                }
                // 返回推送结果
                $connection_admin->send($ret ? 'ok' : 'fail');
            };
            
            $inner_text_worker->listen();
            
            // 进程启动后设置一个每10秒运行一次的定时器
            Timer::add(10, function(){
                global $text_worker;
                $time_now = time();
                foreach($text_worker->connections as $connection) {
                    // 有可能该connection还没收到过消息,则lastMessageTime设置为当前时间
                    if (empty($connection->lastMessageTime)) {
                        $connection->lastMessageTime = $time_now;
                        continue;
                    }
                    // 上次通讯时间间隔大于心跳间隔,则认为客户端已经下线,关闭连接
                    if ($time_now - $connection->lastMessageTime > HEARTBEAT_TIME) {
                        var_dump("delete:" . $connection->uid);
                        unset($text_worker->uidConnections["{$connection->uid}"]);
                        $connection->close();
                    }
                }
            });
        };
        //当浏览器连接的时候触发此函数
        $text_worker->onConnect = function($connection){

        };
        //向用户发送信息的时候触发
        //$connection 当前连接的人的信息 $data 发送的数据
        $text_worker->onMessage = function($connection,$data){
        	// 给connection临时设置一个lastMessageTime属性,用来记录上次收到消息的时间
    		$connection->lastMessageTime = time();
    		// 其它业务逻辑...
            $data = json_decode($data, true);
            if($data['type']=='login'){
                $this->create_uid($connection,$data);
            }
            if($data['type']=='send_message'){
                $this->send_message($connection,$data);
            }
        };
        //浏览器断开链接的时候触发
        $text_worker->onClose = function($connection){};
        Worker::runAll();
    }
    
    //创建uid方法
    public function create_uid($connection,$data){
        global $text_worker;
        $connection->uid = $data['uid'];
        //保存用户的uid
        $text_worker->uidConnections["{$connection->uid}"] = $connection;
        //向自己的浏览器返回创建成功的信息
        $connection->send("用户:[{$connection->uid}] 创建成功");
    }
    
    public function send_message($connection,$data){
        global $text_worker;
        if(isset($data['to_uid'])){
            // var_dump($data['to_uid']);
            if(isset($text_worker->uidConnections["{$data['to_uid']}"])){
                $to_connection=$text_worker->uidConnections["{$data['to_uid']}"];
                $to_connection->send($data['uid'].$data['message']);
            }
        }
    }
}

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/365999.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

2022年度商业关键词调研报告(小红书美妆个护)

美妆个护&#xff0c;作为小红书最火热的领域之一&#xff0c;不断塑造流行新趋势&#xff0c;大量品牌在小红书孵化爆品&#xff0c;崛起破圈。回顾2022年&#xff0c;小红书美妆个护领域有哪些热门新趋势呢&#xff1f; 由此&#xff0c;千瓜推出《2022年度商业关键词调研报告…

机器学习-基于KNN及其改进的汉字图像识别系统

一、简介和环境准备 knn一般指邻近算法。 邻近算法&#xff0c;或者说K最邻近&#xff08;KNN&#xff0c;K-NearestNeighbor&#xff09;分类算法是数据挖掘分类技术中最简单的方法之一。而lmknn是局部均值k最近邻分类算法。 本次实验环境需要用的是Google Colab和Google Dr…

ASEMI高压MOS管60R380参数,60R380特征,60R380应用

编辑-Z ASEMI高压MOS管60R380参数&#xff1a; 型号&#xff1a;60R380 漏极-源极电压&#xff08;VDS&#xff09;&#xff1a;600V 栅源电压&#xff08;VGS&#xff09;&#xff1a;20V 漏极电流&#xff08;ID&#xff09;&#xff1a;11A 功耗&#xff08;PD&#x…

Python的PyQt框架的使用-资源文件夹的使用

Python的PyQt框架的使用-资源文件夹的使用一、前言二、Qt Designer加载资源文件三、资源文件的转换一、前言 个人主页: ζ小菜鸡大家好我是ζ小菜鸡&#xff0c;小伙伴们&#xff0c;让我们一起来学习Python的PyQt框架的使用。如果文章对你有帮助、欢迎关注、点赞、收藏(一键三…

win10怎么取消开机密码?电脑小白也可以轻松掌握的3种方法

为了保护隐私&#xff0c;我们大多喜欢给自己的电脑“上锁”&#xff0c;即设置开机密码。有时候&#xff0c;我们想要取消电脑的开机密码&#xff0c;却不知有什么方法可以帮到我们。win10怎么取消开机密码&#xff1f;方法有很多。 比如&#xff1a;通过运行来进入用户账户设…

Golang学习Day1

&#x1f60b; 大家好&#xff0c;我是YAy_17&#xff0c;是一枚爱好网安的小白。本人水平有限&#xff0c;欢迎各位大佬指点&#xff0c;欢迎关注 &#x1f601;&#xff0c;一起学习 &#x1f497; &#xff0c;一起进步 ⭐ 。⭐ 此后如竟没有炬火&#xff0c;我便是唯一的光…

GEE学习笔记 五十一:Fusion Table将在2019年12月3日关闭

刚刚看到的一则消息&#xff0c;Google运行了9年之久的Fusion Table将在2019年12月3日关闭相关服务&#xff0c;同时也就是未来Google Earth Engine&#xff08;GEE&#xff09;上将不会存在Fusion Table这一数据&#xff0c;GEE官方也不再建议用户使用Fusion Table数据。 目前…

anaconda:安装cuda和对应版本的cudnn

复现别人论文的时候经常遇到不同的cuda版本&#xff0c;可以使用anaconda创建虚拟环境&#xff0c;并在不同的虚拟环境中配置对应的cuda版本 1、安装anaconda及虚拟环境使用 Anaconda多个python版本&#xff08;python2.7 & python3.8&#xff09; 2、安装cuda和对应版本…

【机器学习】噪声数据的理解

文章目录一、噪声数据1.1 分箱1.2 回归1.3 聚类1.4 其他二、数据清理作为一个过程2.1 偏差检测2.1.1 使用“元数据”&#xff1a;关于数据的数据2.1.2 编码格式&#xff1a;存在使用不一致、数据表示不一致2.1.3 字段过载2.1.4 唯一性规则2.1.5 连续性规则2.1.6 空值规则2.2 数…

爆赞!首次公布阿里Java成长路线,Github访问量突破80万

作为程序员&#xff0c;进大厂是大多数人的梦想&#xff0c;进大厂的好处也如下图一样&#xff1a; 有面儿&#xff0c;不易失业。牛人多&#xff0c;培训多&#xff0c;成长更快。钱多。有较为完善的晋升规则。站在巨人肩膀人&#xff0c;眼界开阔更何况程序员不同于其他行业…

zabbix4.0安装部署

目录 1.1、添加 Zabbix 软件仓库 1.2、安装 Server/proxy/前端 1.3、创建数据库 1.4、导入数据 1.5、为 Zabbix server/proxy 配置数据库 1.6、 启动 Zabbix server 进程 1.7、zabbix前端配置 SELinux 配置 1.8、安装 Agent 1.9、启动zabbix 2.0、访问zabbix 1.1、添加…

【图像处理OpenCV(C++版)】——4.6 限制对比度的自适应直方图均衡化

前言&#xff1a; &#x1f60a;&#x1f60a;&#x1f60a;欢迎来到本博客&#x1f60a;&#x1f60a;&#x1f60a; &#x1f31f;&#x1f31f;&#x1f31f; 本专栏主要结合OpenCV和C来实现一些基本的图像处理算法并详细解释各参数含义&#xff0c;适用于平时学习、工作快…

使用FORCE训练的脉冲神经网络中的监督学习(Matlab代码实现)

目录 &#x1f4a5;1 概述 &#x1f4da;2 运行结果 &#x1f389;3 参考文献 &#x1f468;‍&#x1f4bb;4 Matlab代码 &#x1f4a5;1 概述 1.1 脉冲神经网络简介 脉冲神经网络 (SNN) 属于第三代神经网络模型&#xff0c;实现了更高级的生物神经模拟水平。除了神经元和…

3.知识图谱概念和相关技术简介[知识抽取、知识融合、知识推理方法简述],典型应用案例介绍国内落地产品介绍。一份完整的入门指南,带你快速掌握KG知识,芜湖起飞!

1. 知识图谱(KG)的概念 知识图谱(KG)得益于Web的发展(更多的是数据层面),有着来源于KR、NLP、Web、AI多个方面的基因。知识图谱是2012年后的提法,基础还是语义网和本体论。 知识图谱的本质包含: 知识表示——Knowledge Representation基于知识表示的知识库——Knowledge…

OpenGL入门demo

开发环境visual studio 2022 preview版本&#xff0c;x64版本安装OpenGL首先OpenGL是windows系统里面自带的&#xff0c;我们可以不用去下载最新版。直接在此基础上配置OpenGL的三个扩展库glew&#xff0c;glfw&#xff0c;flut就可以了。下载OpenGL的开发依赖类库&#xff1a;…

【java】Spring Cloud --Spring Cloud Alibaba 微服务解决方案

文章目录1、Spring Cloud Alibaba 是什么先说说 Spring CloudSpring Cloud Alibaba和Spring Cloud 的区别和联系Spring Cloud Alibaba2、Spring Cloud Alibaba 包含组件阿里开源组件阿里商业化组件集成 Spring Cloud 组件3、Spring Cloud Alibaba 功能服务注册与发现支持多协议…

python-剑指 Offer 42. 连续子数组的最大和【动态规划经典题解】

一.题目 剑指 Offer 42. 连续子数组的最大和 描述&#xff1a;输入一个整型数组&#xff0c;数组中的一个或连续多个整数组成一个子数组。求所有子数组的和的最大值。 要求时间复杂度为O(n)。 示例1: 输入: nums [-2,1,-3,4,-1,2,1,-5,4] 输出: 6 解释: 连续子数组 [4,-1,2…

html初识

HTML认知 文章目录HTML认知语法规范注释标签组成和关系标签的关系标签学习排版系列标签**标题标签****段落标签**换行标签水平线标签文本格式化标签媒体标签图片标签src 目标图片的路径alt 替换文本title 图片的标题width 宽度 / height 高度路径绝对路径相对路径&#xff08;常…

feature分支开发到一半时切换到bugfix分支,如何暂存数据

1、解决思路在工作过程中&#xff0c;当你正在当前feature分支上进行功能的开发&#xff0c;突然来了一个bug&#xff0c;要创建一个bugfix修复分支进行修复。但是当前feature分支你只开发了一半&#xff0c;显然你去提当前的半成品是不合适的&#xff0c;我们如何处理此类问题…

面试题-----JDBC单例模式(懒汉式和饿汉式)

1.单例概念 作为一种常见的设计模式&#xff0c;单例模式的设计概念是"两个私有,一个公有",即私有属性/成员变量和私有构造,以及公有方法,常用于在整个程序中仅调用一次的代码。 2.具体操作 从单例模式的描述来看,单例模式并不能用于多次频繁调用的设计中,而更适用…