企业🤖AI智能体构建引擎,智能编排和调试,一键部署,支持私有化部署方案 广告
​ 基础文档:[Workerman · ThinkPHP5.0完全开发手册 · 看云](https://www.kancloud.cn/manual/thinkphp5/235128 "Workerman · ThinkPHP5.0完全开发手册 · 看云") 在线测试地址:[EasySwoole-WebSocket在线测试工具](http://www.easyswoole.com/wstool.html "EasySwoole-WebSocket在线测试工具") 一、安装扩展包   composer require topthink/think-worker   遇到报错:不能安装,参考:[tp5 workerman安装不上解决方法 - 知乎](https://zhuanlan.zhihu.com/p/127921818 "tp5 workerman安装不上解决方法 - 知乎")  直接执行:composer require topthink/think-worker=1.0.\*     即可成功 二、新建 server.php   ~~~php #!/usr/bin/env php <?php define('APP_PATH', __DIR__ . '/application/'); define('BIND_MODULE','push/Worker'); // 加载框架引导文件 require __DIR__ . '/thinkphp/start.php'; ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") 三、新建Worker.php    php think make:controller push/Worker   即可,将里面的内容替换如下所示: ~~~php <?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) { } } ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") ~~~html 四、执行 php server.php start 遇到禁用函数就去对应的PHP里面把禁用函数删除 (此命令可以放到Supervisor的守护进程里面去),并且查看端口是否运行,宝塔里面也要放行对应的端口 2346 ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") 配置安全组参考: [回答阿里云websocket配置问题\_慕课手记](https://www.imooc.com/article/72220 "回答阿里云websocket配置问题_慕课手记")    这个很关键 测试远程连接:[http://www.voidcn.com/article/p-nifcqskk-buc.html](http://www.voidcn.com/article/p-nifcqskk-buc.html "http://www.voidcn.com/article/p-nifcqskk-buc.html")       telnet localhost 2346         localhost改成外网ip即可,这个走通了,前端就能直接连接了 linux 退出Telnet命令    先输入命令:CTRL+\]然后再输入命令:quit 五、前端代码 ~~~html <!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> ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") # l 六、前端开两个端口即可进行相互通讯: ~~~javascript ws.send('{"id":"2","msg":"21111111111110"}'); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") 最终效果如下所示: ![](https://img-blog.csdnimg.cn/20201119134210179.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MDUwMzYw,size_16,color_FFFFFF,t_70)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")​编辑![](https://img-blog.csdnimg.cn/20201119134223133.png?x-oss-process=image/watermark,type_ZmFuZ3poZW5naGVpdGk,shadow_10,text_aHR0cHM6Ly9ibG9nLmNzZG4ubmV0L3FxXzM0MDUwMzYw,size_16,color_FFFFFF,t_70)![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动")​编辑 重点在这:因为在这里需要用到服务端给客户端推送,用到了text服务 ## WorkerMan中php后端及时推送消息给客户端 参考连接:[WorkerMan中php后端及时推送消息给客户端-Workerman-PHP中文网](https://www.php.cn/workerman/442367.html "WorkerMan中php后端及时推送消息给客户端-Workerman-PHP中文网") php后端及时推送消息给客户端 原理: 1、建立一个websocket Worker,用来维持客户端长连接 2、websocket Worker内部建立一个text Worker 3、websocket Worker 与 text Worker是同一个进程,可以方便的共享客户端连接 4、某个独立的php后台系统通过text协议与text Worker通讯 5、text Worker操作websocket连接完成数据推送 代码及步骤 ~~~php 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); ~~~ ![](data:image/gif;base64,R0lGODlhAQABAPABAP///wAAACH5BAEKAAAALAAAAAABAAEAAAICRAEAOw== "点击并拖拽以移动") 后端推送消息的代码和push.php监听同一个端口 push.php和前端监听同一个websocket端口 通过后端推送消息的代码向push.php推送数据, push.php接受到数据后通过处理 利用websocket往前端推送数据 参考: [WorkerMan中php后端及时推送消息给客户端](https://www.php.cn/workerman/442367.html "WorkerMan中php后端及时推送消息给客户端") [workerman 内部系统推送数据](https://www.baidu.com/s?ie=utf-8&f=8&rsv_bp=1&tn=baidu&wd=workerman%20%20%E5%86%85%E9%83%A8%E7%B3%BB%E7%BB%9F%E6%8E%A8%E9%80%81%E6%95%B0%E6%8D%AE&oq=php%2520%25E8%25BF%259E%25E6%258E%25A5websocket%2520%25E5%258F%2591%25E9%2580%2581%25E6%25B6%2588%25E6%2581%25AF&rsv_pq=ad92a8a1000d2a5c&rsv_t=e510BtJ8mcMjXrby2cxwKlWpOqVkeKijK0iPv%2FBLWnxZSEO6u1pQGrcICIA&rqlang=cn&rsv_enter=1&rsv_dl=tb&rsv_btype=t&inputT=12542&rsv_sug3=97&rsv_sug1=64&rsv_sug7=100&rsv_sug2=0&rsv_sug4=13298 "workerman 内部系统推送数据") [Centos7开放端口及查看端口](https://www.cnblogs.com/heqiuyong/p/10460150.html "Centos7开放端口及查看端口")   ​