jcbowen / yiiswoole
Yii websocket server component
Installs: 49
Dependents: 0
Suggesters: 0
Security: 0
Stars: 1
Watchers: 1
Forks: 1
Open Issues: 0
Type:yii2-extension
Requires
- php: >=7.4
- ext-json: *
- ext-posix: *
- jcbowen/jcbase-yii2: ^0.x-dev || >=0.3.4
- yiisoft/yii2: ^2.0
README
介绍
Yii2 + Swoole4
目前实现了websocket服务器,同时支持了高性能共享内存Table的使用
不兼容说明
使用了usleep,所以不兼容win环境
前提
服务器中需要安装swoole4
(如果是宝塔搭建的环境,直接在使用的php版本中安装swoole4扩展)
由于某些跟踪调试的 PHP 扩展大量使用了全局变量,可能会导致 Swoole 协程发生崩溃。请关闭以下相关扩展:
xdebug、phptrace、aop、molten、xhprof、phalcon(Swoole 协程无法运行在 phalcon 框架中)
安装教程
composer执行
composer require "jcbowen/yiiswoole"
或者在 composer.json
加入
"jcbowen/yiiswoole": "^4.0"
配置说明
在console/config/main.php
的controllerMap中加入配置
'websocket' => [ 'class' => \Jcbowen\yiiswoole\websocket\console\controllers\WebSocketController::class, 'serverClass' => \Jcbowen\yiiswoole\websocket\console\components\Server::class, // 可不填,默认值 'serverConfig' => [ 'daemonize' => true,// 守护进程执行 'heartbeat_check_interval' => 60, // 启用心跳检测,默认为false 'heartbeat_idle_time' => 120, // 连接最大允许空闲的时间,启用心跳检测的情况下,如未设置,默认未心跳检测的两倍 'pid_file' => '@runtime/yiiswoole/websocket.pid', 'log_file' => '@runtime/yiiswoole/websocket.log', 'log_level' => SWOOLE_LOG_ERROR, 'buffer_output_size' => 2 * 1024 * 1024, //配置发送输出缓存区内存尺寸 'worker_num' => 1, 'max_wait_time' => 60, 'reload_async' => true, ], 'serverPorts' => [ // 第一个为websocket主服务 'ws' => [ 'host' => '0.0.0.0', 'port' => 9408, 'cert' => false, // 证书类型 默认值:false 其它值:'ssl' ] ], 'tablesConfig' => [], ],
使用
# 启动 php yii websocket/start # 停止 php yii websocket/stop # 重启 php yii websocket/restart
运行说明
websocket客户端向服务器发送json字符串,如:
如果握手的时候,携带了目录路径,该路径将会作为route缓存起来;请求中如果携带了route字段,则替换缓存中的route
{ "route": "site/test", "message": "这是一条来自websocket客户端的消息" }
通过执行\Jcbowen\yiiswoole\components\ContactData::get($fd, '_B');
方法,可以读取上下文中缓存的信息;
通过执行\Jcbowen\yiiswoole\components\ContactData::get($fd,'_GPC');
方法,可以读取上下文中缓存的get数据;
其中server和frame会被缓存到_B
中;
接收到的json会被转为数组后缓存到_GPC
中;
携带的目录代表的是监听到动作后转发到哪个路由(由于通过console运行的进程,所以这里的路由指的是console里的路由)。
// 这里展示onMessage的源码,用来理解实现原理 public function onMessage(WsServer $server, Frame $frame) { $_B = ContactData::get($frame->fd, '_B'); $_GPC = ContactData::get($frame->fd, '_GPC'); // 修改上下文中的信息 $_B['WebSocket']['on'] = 'message'; $jsonData = Util::isJson($frame->data) ? (array)@json_decode($frame->data, true) : $frame->data; $jsonData = $jsonData ?: $frame->data; // 避免因json解析失败导致数据丢失的情况 // 空数据为触发心跳 if (empty($jsonData)) return $server->push($frame->fd, json_encode([ 'errcode' => ErrCode::SUCCESS, 'errmsg' => 'Heart Success' ], JSON_UNESCAPED_UNICODE)); if (is_array($jsonData)) { $_GPC = ArrayHelper::merge((array)$_GPC, $jsonData); $route = trim($_GPC['route']) ?: ''; // 如果route不存在,不知道应该由哪个路由进行处理,只能进行报错处理 if (empty($route)) return $server->push($frame->fd, json_encode([ 'errcode' => ErrCode::PARAMETER_ERROR, 'errmsg' => 'Empty Route', ], JSON_UNESCAPED_UNICODE)); // 更新上下文中的信息 ContactData::set($frame->fd, '_B', $_B); ContactData::set($frame->fd, '_GPC', $_GPC); // 根据json数据中的路由转发到控制器内进行处理 try { return Yii::$app->runAction($route, [$server, $frame, $frame->fd, $this]); } catch (Exception $e) { Yii::info($e); $this->Controller->stdout($e->getMessage() . PHP_EOL, BaseConsole::FG_RED); return false; } } else { // 数据格式错误 return $server->push($frame->fd, json_encode([ 'errcode' => ErrCode::ILLEGAL_FORMAT, 'errmsg' => 'Data Format Error', ], JSON_UNESCAPED_UNICODE)); } }
总结:jcbowen/yiiswoole插件会在websocket触发onmessage时,根据route调用对应的控制器方法,并将websocket服务和接收到数据分别存放到_B
与_GPC
中;
在控制器方法中使用
class SiteController extends Controller { use Jcbowen\yiiswoole\websocket\console\components\Server; use Swoole\WebSocket\Frame; use Swoole\WebSocket\server as WsServer; public function actionTest(WsServer $WsServer, Frame $frame,Server $server) { $_B = ContactData::get($frame->fd, '_B'); $_GPC = ContactData::get($frame->fd, '_GPC'); /** @var \Swoole\Table $table */ $table = $server->_tables['test_table']; return $WsServer->push($frame->fd, $_GPC['message']); } }