目录
- 一、安装扩展包 composer require topthink/think-worker
- 二、新建 server.php
- 三、新建Worker.php
- 四、执行 php server.php start
- 五、前端代码
- 六、前端开两个端口即可进行相互通讯:
- WorkerMan中php后端及时推送消息给客户端
本文介绍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往前端推送数据
暂无评论内容