经验首页 前端设计 程序设计 Java相关 移动开发 数据库/运维 软件/图像 大数据/云计算 其他经验
当前位置:技术经验 » 程序设计 » PHP » 查看文章
【workerman】uniapp+thinkPHP5使用GatewayWorker实现实时通讯
来源:cnblogs  作者:我恨bug  时间:2024/7/11 11:15:48  对本文有异议

前言

之前公司需要一个内部的通讯软件,就叫我做一个。通讯软件嘛,就离不开通讯了,然后我就想到了长连接。这里本人用的是GatewayWorker框架。

什么是GatewayWorker框架?

GatewayWorker是基于Workerman开发的一套TCP长连接的应用框架,实现了单发、群发、广播等接口,内置了mysql类库,GatewayWorker分为Gateway进程和Worker进程,支持分布式部署,能够支持大量的连接数。

GatewayWorker的工作原理

image

  1. 1、启动所有进程(GatewayWorkerbusinessregister
  2. 2GatewayWorkerbusiness进程启动后向register请求注册
  3. 3register服务收到注册请求后,把所有Gateway的通讯地址保存在内存中同时把内存中所有的Gateway的通讯地址发给business
  4. 4business进程得到所有的Gateway内部通讯地址后进行连接GatewayWorker
  5. 5、如果有新的GatewayWorker服务进行register,则将新的Gateway内部通讯地址列表将广播给所有buiness并建立连接
  6. 6、如果有GatewayWorker下线,则Register服务会收到通知,会将该GatewayWorker内部通讯地址删除,然后广播新的内部通讯地址列表给所有business
  7. 7、此时GatewayWorkerbuiness已经建立起长连接
  8. 8、客户端的事件及接受的数据全部由GatewayWorker转发给business进行处理。

目录结构

  1. ├── Applications // 项目应用目录
  2. └── YourAppGateway // 建立一个存放workman的目录,名字随意
  3. ├── Events.php // 处理主逻辑业务的文件,管理onConnect onMessage onClose 等方法
  4. ├── start_gateway.php // gateway进程启动脚本、配置服务注册地址、端口号、进程数等参数
  5. ├── start_businessworker.php // 用户进程的启动脚本
  6. └── start_register.php // 注册服务的启动脚本
  7. ├── start.php // 全局启动脚本,此脚本会依次加载Applications/YourAppGateway/start*.php对所有脚本进行启动
  8. └── vendor // GatewayWorker框架和Workerman框架源码目录

GatewayWorker实现

以宝塔为例

1.安装composer

登录SSH终端,使用以下命令下载Composer的安装脚本:

  1. php -r "copy('https://getcomposer.org/installer', 'composer-setup.php');"

运行下面的命令来安装Composer:

  1. php composer-setup.php --install-dir=/usr/local/bin --filename=composer

检查composer版本

  1. composer -v //检查composer版本

2.安装workerman

在项目根目录打开宝塔终端,输入以下命令安装workman

  1. composer require topthink/think-worker

3.安装GatewayWorker

在项目根目录打开宝塔终端,输入以下命令安装GatewayWorker

  1. composer require workerman/gateway-worker

4.实现代码

可以选择官方提供的demo 链接:http://www.workerman.net/download/GatewayWorker.zip

或者使用我根据demo改编而来的

先在项目应用目录(一般是Applications)下新建一个文件存储以下四个进程文件

start_gateway.php

  1. <?php
  2. use \Workerman\Worker;
  3. use \Workerman\WebServer;
  4. use \GatewayWorker\Gateway;
  5. use \GatewayWorker\BusinessWorker;
  6. use \Workerman\Autoloader;
  7. // 自动加载类
  8. require_once __DIR__ . '/../../vendor/autoload.php';
  9. // gateway 进程,这里使用Text协议,可以用telnet测试
  10. $gateway = new Gateway("websocket://0.0.0.0:8283");
  11. // gateway名称,status方便查看
  12. $gateway->name = 'YourAppGateway';
  13. // gateway进程数
  14. $gateway->count = 200;
  15. // 本机ip,分布式部署时使用内网ip
  16. $gateway->lanIp = '127.0.0.1';
  17. // 内部通讯起始端口,假如$gateway->count=4,起始端口为4000
  18. // 则一般会使用4000 4001 4002 4003 4个端口作为内部通讯端口
  19. $gateway->startPort = 2900;
  20. // 服务注册地址、端口
  21. $gateway->registerAddress = '127.0.0.1:1237';
  22. // 心跳间隔
  23. //$gateway->pingInterval = 10;
  24. // 心跳数据
  25. //$gateway->pingData = '{"type":"ping"}';
  26. /*
  27. // 当客户端连接上来时,设置连接的onWebSocketConnect,即在websocket握手时的回调
  28. $gateway->onConnect = function($connection)
  29. {
  30. $connection->onWebSocketConnect = function($connection , $http_header)
  31. {
  32. // 可以在这里判断连接来源是否合法,不合法就关掉连接
  33. // $_SERVER['HTTP_ORIGIN']标识来自哪个站点的页面发起的websocket链接
  34. if($_SERVER['HTTP_ORIGIN'] != 'http://kedou.workerman.net')
  35. {
  36. $connection->close();
  37. }
  38. // onWebSocketConnect 里面$_GET $_SERVER是可用的
  39. // var_dump($_GET, $_SERVER);
  40. };
  41. };
  42. */
  43. // 如果不是在根目录启动,则运行runAll方法
  44. if(!defined('GLOBAL_START'))
  45. {
  46. Worker::runAll();
  47. }

start_businessworker.php

  1. <?php
  2. use Workerman\Worker;
  3. use Workerman\WebServer;
  4. use GatewayWorker\Gateway;
  5. use GatewayWorker\BusinessWorker;
  6. use Workerman\Autoloader;
  7. // 自动加载类
  8. require_once __DIR__ . '/../../vendor/autoload.php';
  9. // bussinessWorker 进程
  10. $worker = new BusinessWorker();
  11. // worker名称
  12. $worker->name = 'YourAppBusinessWorker';
  13. // bussinessWorker进程数量
  14. $worker->count = 200;
  15. // 服务注册地址、端口
  16. $worker->registerAddress = '127.0.0.1:1237';
  17. // 如果不是在根目录启动,则运行runAll方法
  18. if(!defined('GLOBAL_START'))
  19. {
  20. Worker::runAll();
  21. }

start_register.php

  1. <?php
  2. use \Workerman\Worker;
  3. use \GatewayWorker\Register;
  4. // 自动加载类
  5. require_once __DIR__ . '/../../vendor/autoload.php';
  6. // register 必须是text协议 1237为端口
  7. $register = new Register('text://0.0.0.0:1237');
  8. // 如果不是在根目录启动,则运行runAll方法
  9. if(!defined('GLOBAL_START'))
  10. {
  11. Worker::runAll();
  12. }

Events.php

  1. <?php
  2. use \GatewayWorker\Lib\Gateway;
  3. /**
  4. * 主逻辑
  5. * 主要是处理 onConnect onMessage onClose 三个方法
  6. */
  7. class Events
  8. {
  9. /**
  10. * 当客户端连接时触发
  11. *
  12. * @param int $client_id 连接id
  13. */
  14. public static function onConnect($client_id)
  15. {
  16. echo "【新的客户端链接】:client_id:".$client_id.PHP_EOL;
  17. // 向当前client_id发送数据
  18. Gateway::sendToClient($client_id, "");
  19. // 向所有人发送
  20. $data=[
  21. 'client_id'=>$client_id,
  22. 'message'=>'欢迎'.$client_id.'登录!',
  23. 'data'=>[]
  24. ];
  25. Gateway::sendToAll(json_encode($data));
  26. // Gateway::sendToAll("$client_id login\r\n");
  27. }
  28. /**
  29. * 当客户端发来消息时触发
  30. * @param int $client_id 连接id
  31. * @param mixed $message 具体消息
  32. */
  33. public static function onMessage($client_id, $message){
  34. $data=[
  35. 'client_id'=>$client_id,
  36. 'message'=>$client_id.'说:'.$result['message'],
  37. 'data'=>$message
  38. ];
  39. Gateway::sendToAll(json_encode($data));
  40. // 向所有人发送
  41. // Gateway::sendToAll("$client_id said $message\r\n");
  42. }
  43. /**
  44. * 当用户断开连接时触发
  45. * @param int $client_id 连接id
  46. */
  47. public static function onClose($client_id)
  48. {
  49. // 向所有人发送
  50. // GateWay::sendToAll("$client_id 退出了!\r\n");
  51. }
  52. }

随后在项目的根目录下新建一个启动文件

start_all_workman.php

  1. <?php
  2. ini_set('display_errors', 'on');
  3. use Workerman\Worker;
  4. if(strpos(strtolower(PHP_OS), 'win') === 0)
  5. {
  6. exit("start.php not support windows, please use start_for_win.bat\n");
  7. }
  8. // 检查扩展
  9. if(!extension_loaded('pcntl'))
  10. {
  11. exit("Please install pcntl extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
  12. }
  13. if(!extension_loaded('posix'))
  14. {
  15. exit("Please install posix extension. See http://doc3.workerman.net/appendices/install-extension.html\n");
  16. }
  17. // 标记是全局启动
  18. define('GLOBAL_START', 1);
  19. require_once __DIR__ . '/vendor/autoload.php';
  20. // 加载所有Applications/*/start.php,以便启动所有服务
  21. foreach(glob(__DIR__.'/application/此处请改成你自己命名存放workman的目录名/start*.php') as $start_file)
  22. {
  23. require_once $start_file;
  24. }
  25. // 运行所有服务
  26. Worker::runAll();

注意开启端口后记得去放行端口!!!除了宝塔放行以外,你的服务器(阿里云/腾讯云等等)也记得要去放行!!!

启动workman

在项目根目录下打开终端,输入php start_all_workman.php start -d ,开启守护进程,如果出现一下页面即成功开启

image

如想关闭workman进程则输入php start_all_workman.php stop 进行关闭

GatewayWorker使用

如果你的网站使用的是Https协议的话,WebSocket必须使用wss协议
但是wss协议不支持IP:端口的形式,而是只能写域名+url
所以为了解决使用https协议而WebSocket不能连接的问题,可以使用Nginx进行反向代理
在网站配置文件的server下加入以下代码

  1. location /connectWorkman(名字随你取,别跟其他反向代理重名就行)
  2. {
  3. proxy_pass http://127.0.0.1:8283;
  4. proxy_http_version 1.1;
  5. proxy_set_header Upgrade $http_upgrade;
  6. proxy_set_header Connection "Upgrade";
  7. proxy_set_header X-Real-IP $remote_addr;
  8. }

前端使用(uniapp)

  1. init() {
  2. SocketTask = uni.connectSocket({
  3. url: 'wss://chat.gdpaimaihui.com/auction', //正式
  4. header: {
  5. 'content-type': 'application/json'
  6. },
  7. success: function(res) {
  8. console.log('WebSocket连接创建', res);
  9. },
  10. fail: function(err) {
  11. uni.showToast({
  12. title: '网络异常!',
  13. icon: 'none'
  14. });
  15. console.log(err);
  16. }
  17. });
  18. //websocket监听事件
  19. SocketTask.onOpen((res) => {
  20. socketOpen = true
  21. canReconnect = true
  22. console.log('监听 WebSocket 连接打开事件。', res);
  23. //websocket连接后可以启动个定时器,每隔一段时间进行心跳一次,以防心跳停止断开连接
  24. this.timer = setInterval(() => {
  25. SocketTask.send({
  26. data: '心跳',
  27. success() {
  28. // console.log('发送心跳成功');
  29. }
  30. })
  31. }, 2000)
  32. });
  33. SocketTask.onError((onError) => {
  34. console.log('监听 WebSocket 错误。错误信息', onError);
  35. socketOpen = false;
  36. if (canReconnect) {
  37. this.reconnect()
  38. canReconnect = false
  39. }
  40. });
  41. SocketTask.onMessage((res) => {
  42. console.log('监听WebSocket接受到服务器的消息事件。服务器返回的消息', res);
  43. });
  44. },
  45. //重新连接
  46. reconnect() {
  47. if (!socketOpen) {
  48. let count = 0;
  49. reconnectInterval = setInterval(() => {
  50. console.log("正在尝试重连")
  51. uni.showToast({
  52. title: '正在尝试重连',
  53. icon: 'none'
  54. })
  55. this.init();
  56. count++
  57. console.log();
  58. //重连一定次数后就不再重连
  59. if (count >= reconnectTimes) {
  60. clearInterval(reconnectInterval)
  61. uni.showToast({
  62. title: '网络异常或服务器错误',
  63. icon: 'none'
  64. })
  65. }
  66. }, reconnectDelay)
  67. }
  68. }

上述为之前给公司做内部通讯软件时个人整理内容,水平有限,如有错误之处,望各位园友不吝赐教!如果觉得不错,请点击推荐和关注!谢谢~??????? [鲜花][鲜花][鲜花]

原文链接:https://www.cnblogs.com/nothavebug/p/18286356

 友情链接:直通硅谷  点职佳  北美留学生论坛

本站QQ群:前端 618073944 | Java 606181507 | Python 626812652 | C/C++ 612253063 | 微信 634508462 | 苹果 692586424 | C#/.net 182808419 | PHP 305140648 | 运维 608723728

W3xue 的所有内容仅供测试,对任何法律问题及风险不承担任何责任。通过使用本站内容随之而来的风险与本站无关。
关于我们  |  意见建议  |  捐助我们  |  报错有奖  |  广告合作、友情链接(目前9元/月)请联系QQ:27243702 沸活量
皖ICP备17017327号-2 皖公网安备34020702000426号