workerman 实现推送实时数据到前端

2024-05-16 08:28

本文主要是介绍workerman 实现推送实时数据到前端,希望对大家解决编程问题提供一定的参考价值,需要的开发者们随着小编来一起学习吧!

首先要说明一下,实时推送是有两个socket服务端和两个socket的客户端
我们分别起名叫做
外层服务端 (开启服务主进程 创建一个 websoket 连接)ws://
内层服务端 (在开户主进程的时候,内部又创建的一个 socket)text://
外层客户端 (这是由前端的用户端 使用js 创建的一个 websocket 客户端)
内层客户端 (这是php 使用 stream_socket_client 创建的一个客户端)

上代码(说明在代码的注释中)
服务端代码 (websocket 的创建)

  1. 安装 workerman (因为使用的是 tp框架,所以安装的 topthink/think-worker ,它已经包含了 workerman)
composer install topthink/think-worker

这个服务端是一个 挂起的操作, 所以不需要过 tp 的入口文件,直接使用 php 命令行运行就可以了
本人在 app\worker\Testworker.php 新建了这个服务端的文件

require __DIR__ . '/../../vendor/autoload.php';   //因为是独立运行的,所以要引入 autoload.php
use Workerman\Worker;
$worker = new Worker("websocket://0.0.0.0:2345");   //创建了一个 websocket 的服务端,端口是 2345 (记得打开阿里云 或 宝塔的 2345端口)
$worker->count=1; //count属性windows中的配置就写1  linux中可以配多个$worker->uidConnections = [];  //自定义了一个空数组,用来存放所有的 连接对象//当连接成功时
$worker->onConnect = function($connection){dump($connection);//这里只是显示一下连接是否成功,没有什么作用
};//workerman 收到消息时的监听,前端可能发过来不同种类的消息,所以消息上都带有type字段,根据 type 类型的不同做相应的处理
//参数 $connection 表示 这个 websocket连接的资源操作符 是一个非常有用的数据后续我们是在保存起来的
//参数 $msg 就是前端发过来的数据 格式是 {type:"bind",uid:1},自已定义就可以了 我这里bind就是绑定用户的意思
$worker->onMessage = function($connection,$msg) use ($worker){$msg = json_decode($msg,true);if($msg["type"] == "bind"){//如果前端传来的消息类型是 bind 就是绑定用户的消息类型if(!isset($connection->uid)){$connection->uid = $msg["uid"];  //我们给 $connection对象加上一个uid属性 这一步用户不太大}$worker->uidConnections[$connection->uid] = $connection;  //这里把所有客户端的连接放入一个数组中, 并使用 用户的uid 做为键名,当要发送信息的时候,就通过 uid 得到用户相对应的 connection }else if($msg["type"] == "logout"){//如果前端传来的消息类型是 logout 就从uidConnections中删除 相应的connectionunset($worker->uidConnections[$msg["uid"]]);}//这里根据消息类型的不同可以写很多的 if 分支
};//当服务端关闭的时候,给每一个客户端发送 服务器下线的通知
$worker->onClose = function() use ($worker){foreach($worker->uidConnections as $uidConnection){$uidConnection->send("服务器下线了");}
};//当服务启动时
$worker->onWorkerStart = function($worker){
//当服务启动时, 我们在内部开启一个socket  也就是上面说的 内层服务端 端口是2346 记得打开(阿里云或宝塔的端口)
//这里的 innerSocket是给 php 开启的 socket 的客户端来使用的$innerSocket = new Worker("text://0.0.0.0:2346");$innerSocket->onMessage = function($connection,$innermsg) use($worker){//在innermesg 中包含的有数据 {type:"notify",uids:[1,2,4,5],data:{.....}}  类型和要接收通知的用户的id$innermsgArr = json_decode($innermsg,true);if($innermsgArr["type"] == "notify"){//如果消息类型是 notify ,就从 $worker->uidConnections中取出相对应用户的连接对象,使用send方法把数据发送给前端foreach($innermsgArr["uids"] as $uid){$worker->uidConnections[$uid]->send($innermsgArr["data"]);}}}//workerman允许在服务运行过程中调用new Worker实例化Worker建立监听其它端口。此时因为已经在运行,所以不需要调用run方法,直接调用listen方法,将新的监听add到EventLoop中即可。$innserSocket->listen();   //上面解释了这里为什么要使用 listen方法
}Worker::runAll();  //这是workerman 启动服务的方法

以上就是 服务端的代码, 使用时 在命令行中 php Testwroker.php就可以了, 它是一个挂起的窗口,我们实际上线时可以让它在后台运行就可以了


前端(用户端) 客户端 socket的使用 这里我使用的是vue3 不用安装就直接使用js 原生的websocket ,开发工具写的时候可能为 自动给你添加了别了 websocket类,注意把开发工具添加的 import …websocket 这行给删除掉
上代码

<template><div class="wrapper"><h5>这是一个websocket 的测试的例子</h5><div>{{backdata}}</div></div>
</template><script setup>import {onMounted, ref} from "vue";let websocket = ref();let backdata = ref("xxxxx");  //后端发回过来的数据onMounted(()=>{//前端创建一个websocket客户端,并建接到 外层服务端 2345 的端口websocket.value = new WebSocket("ws://127.0.0.1:2345")websocket.value.onopen = ()=> {console.log("建立连接");//当连接建立成功之后, 就给服务端发送一个绑定的消息,其中 uid 是用户登录的时候就已经获取到的, 样例这里我是写死的 为1websocket.value.send(JSON.stringify({type:"bind",uid:1}))}//当后端给前端发送消息时websocket.value.onmessage = (event) =>{console.log(event);console.log(event.data);  //这里的data 也可以 分为不同的类型, 也就是说在服务端返回数据的时候加上 type 自定义一下就可以了,根据类型的不同来前端显示不同的数据backdata.value = event.data;}websocket.value.onclose  = ()=> {console.log("连接关闭")}})</script>

php 的创建的客户端的用法
当我们后端改动数据库之后, 给前端发送一个信息
我们在 自已的业务中 api 添加一个接口

<?php
namespace app\api\controller;use app\common\controller\Frontend;
use utils\WechatUtil;
class Order extends Frontend{protected array $noNeedPermission = ["*"];protected array $noNeedLogin = ["*"];//我们的 api 测度接口public function workerMantest(){/*** 这里有一些修改数据库的代码 根据业务的不同写上就可以了* 完成了数据库的操作之后* 下面要用 php 新建一个 socket的客户端,去连接  innerworker 的服务端,并传输一些数据*///这里使用 stream_socket_client创建了一个 socket 的资源, 为什么要用它,后面再说$client = stream_socket_client("tcp://127.0.0.1:2346",$error,$errmsg,10);if(!$client){echo $errmsg;  //如果报错返回错误信息}else{$result = ["type"=>"notify","uids"=>[1],   // 这里是 哪些用户需要被推送消息"data"=>"这里是想发送给 用户客户端的一些数据"];$resultjson = json_encode($result);  //2346 socket开的是 text://   协议   text协议,要求在文本最后加上 \n 为结束标记fwrite($client, $resultjson."\n");//fread($client,strlen($resultjson."\n"));  这里的 fread就不需要了, 没用,而耗时fclose($client);   //完成关闭资源}return $this->succcess("订单修改成功");}
}

最后在这里说明一下, 为什么 在 php 客户端中使用的是 stream_socket_client 函数创建的,而不是用 workerman 来创建客户端, 我们知道 workerman 也可以创建客户端,也可以创建服务端, 那么我们为什么没有 在 api 接口中,使用workerman 创建客户端呢???

主要是因为 api接口和 workerman 运行的环境不同
api接口,我们是运行在 php-fpm 的环境中的
workerman 是运行在 命令行中的,不能在 php-fpm 中使用

退一步想,我们 api接口运行完成,就返回数据了, 如果在其中使用 workerman的话,这个接口就会一直挂起

所以 我们 使用了 stream_socket_client 创建了 php 端的 socket

这篇关于workerman 实现推送实时数据到前端的文章就介绍到这儿,希望我们推荐的文章对编程师们有所帮助!



http://www.chinasem.cn/article/994383

相关文章

基于SpringBoot+Mybatis实现Mysql分表

《基于SpringBoot+Mybatis实现Mysql分表》这篇文章主要为大家详细介绍了基于SpringBoot+Mybatis实现Mysql分表的相关知识,文中的示例代码讲解详细,感兴趣的小伙伴可... 目录基本思路定义注解创建ThreadLocal创建拦截器业务处理基本思路1.根据创建时间字段按年进

前端高级CSS用法示例详解

《前端高级CSS用法示例详解》在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交互和动态效果的关键技术之一,随着前端技术的不断发展,CSS的用法也日益丰富和高级,本文将深... 前端高级css用法在前端开发中,CSS(层叠样式表)不仅是用来控制网页的外观和布局,更是实现复杂交

Python将博客内容html导出为Markdown格式

《Python将博客内容html导出为Markdown格式》Python将博客内容html导出为Markdown格式,通过博客url地址抓取文章,分析并提取出文章标题和内容,将内容构建成html,再转... 目录一、为什么要搞?二、准备如何搞?三、说搞咱就搞!抓取文章提取内容构建html转存markdown

Python获取中国节假日数据记录入JSON文件

《Python获取中国节假日数据记录入JSON文件》项目系统内置的日历应用为了提升用户体验,特别设置了在调休日期显示“休”的UI图标功能,那么问题是这些调休数据从哪里来呢?我尝试一种更为智能的方法:P... 目录节假日数据获取存入jsON文件节假日数据读取封装完整代码项目系统内置的日历应用为了提升用户体验,

在React中引入Tailwind CSS的完整指南

《在React中引入TailwindCSS的完整指南》在现代前端开发中,使用UI库可以显著提高开发效率,TailwindCSS是一个功能类优先的CSS框架,本文将详细介绍如何在Reac... 目录前言一、Tailwind css 简介二、创建 React 项目使用 Create React App 创建项目

vue使用docxtemplater导出word

《vue使用docxtemplater导出word》docxtemplater是一种邮件合并工具,以编程方式使用并处理条件、循环,并且可以扩展以插入任何内容,下面我们来看看如何使用docxtempl... 目录docxtemplatervue使用docxtemplater导出word安装常用语法 封装导出方

SpringBoot3实现Gzip压缩优化的技术指南

《SpringBoot3实现Gzip压缩优化的技术指南》随着Web应用的用户量和数据量增加,网络带宽和页面加载速度逐渐成为瓶颈,为了减少数据传输量,提高用户体验,我们可以使用Gzip压缩HTTP响应,... 目录1、简述2、配置2.1 添加依赖2.2 配置 Gzip 压缩3、服务端应用4、前端应用4.1 N

SpringBoot实现数据库读写分离的3种方法小结

《SpringBoot实现数据库读写分离的3种方法小结》为了提高系统的读写性能和可用性,读写分离是一种经典的数据库架构模式,在SpringBoot应用中,有多种方式可以实现数据库读写分离,本文将介绍三... 目录一、数据库读写分离概述二、方案一:基于AbstractRoutingDataSource实现动态

Python FastAPI+Celery+RabbitMQ实现分布式图片水印处理系统

《PythonFastAPI+Celery+RabbitMQ实现分布式图片水印处理系统》这篇文章主要为大家详细介绍了PythonFastAPI如何结合Celery以及RabbitMQ实现简单的分布式... 实现思路FastAPI 服务器Celery 任务队列RabbitMQ 作为消息代理定时任务处理完整

Java枚举类实现Key-Value映射的多种实现方式

《Java枚举类实现Key-Value映射的多种实现方式》在Java开发中,枚举(Enum)是一种特殊的类,本文将详细介绍Java枚举类实现key-value映射的多种方式,有需要的小伙伴可以根据需要... 目录前言一、基础实现方式1.1 为枚举添加属性和构造方法二、http://www.cppcns.co