123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289 |
- <?php
- /**
- * FastIm v1.0.0
- * FastAdmin企业IM客服系统
- * https://www.fastadmin.net/store/fastim.html
- *
- * Copyright 2021 FastAdmin:thinkphp
- *
- * FastAdmin企业IM客服系统不是开源产品,所有文字、图片、样式、风格等版权归企业IM客服系统作者所有,如有复制、仿冒、抄袭、盗用,FastAdmin和企业IM客服系统作者将追究法律责任
- *
- * Released on: November 8, 2021
- */
- namespace addons\fastim\library\swoole;
- use think\Db;
- use Swoole\Server\Helper;
- use Swoole\WebSocket\Frame;
- use Swoole\WebSocket\Server as WebSocketServer;
- use Exception;
- use Swoole\Timer;
- /**
- * Swoole WebSocket Server 命令行服务类
- * 此文件的修改不支持热更新,请于更新后重启swoole-websocket服务
- */
- class WebSocket
- {
- protected $monitor;
- protected $lastMtime;
- protected $config;
- /**
- * Swoole对象
- * @var object
- */
- protected $swoole;
- /**
- * Socket的类型
- * @var int
- */
- protected $sockType = SWOOLE_SOCK_TCP;
- /**
- * 运行模式
- * @var int
- */
- protected $mode = SWOOLE_PROCESS;
- /**
- * swooleCommon 类实例
- */
- protected $swooleCommon;
- /**
- * 支持的响应事件
- * @var array
- */
- protected $event = [
- 'Start',
- 'Shutdown',
- 'WorkerStart',
- 'WorkerStop',
- 'WorkerExit',
- 'Close',
- 'Task',
- 'Finish',
- 'PipeMessage',
- 'WorkerError',
- 'ManagerStart',
- 'ManagerStop',
- 'Open',
- 'Message',
- 'HandShake',
- 'Request'
- ];
- function __construct()
- {
- $this->config = get_addon_config('fastim');
- if ($this->config['wss_switch']) {
- if (file_exists($this->config['ssl_cert_file']) && file_exists($this->config['ssl_key_file'])) {
- $this->sockType = SWOOLE_SOCK_TCP | SWOOLE_SSL;
- } else {
- throw new Exception('SSL certificate file does not exist!');
- }
- }
- $this->swoole = new WebSocketServer('0.0.0.0', $this->config['websocket_port'], $this->mode, $this->sockType);
- }
- /**
- * Worker 进程启动
- * @param $server
- * @param $worker_id
- */
- public function onWorkerStart($server, $worker_id)
- {
- $this->lastMtime = time();
- if (0 == $worker_id && $this->monitor) {
- $this->monitor($server);
- }
- // print_r(get_included_files());// 查看不支持热更新的文件列表
- // 保持mysql链接可用性
- $server->tick(27000, function () {
- Db::execute("SELECT 1");
- });
- }
- /**
- * 链接握手成功
- * @param $server
- * @param $frame
- */
- public function onOpen($ws, $request)
- {
- $ws->push($request->fd, json_encode([
- 'event' => 'open'
- ]));
- }
- /**
- * 收到数据帧
- * @param $server
- * @param $frame
- */
- public function onMessage($server, $frame)
- {
- $data = json_decode($frame->data, true);
- // 安全检查过滤
- array_walk_recursive($data, ['addons\fastim\library\Common', 'checkVariable']);
- if (!is_array($data) || !isset($data['c']) || !isset($data['a'])) {
- $server->push($frame->fd, json_encode([
- 'event' => 'show_msg',
- 'data' => '错误的请求!',
- 'close' => true
- ]));
- $server->close($frame->fd);
- return;
- }
- // 载入文件类似:根目录/addons/fastim/library/controller/index.php
- $filename = __DIR__ . '/../controller/' . $data['c'] . '.php';
- if (file_exists($filename)) {
- require_once $filename;
- // 检查要访问的类是否存在
- if (!class_exists($data['c'], false)) {
- $server->push($frame->fd, json_encode([
- 'event' => 'show_msg',
- 'data' => '访问的控制器不存在!',
- 'close' => true
- ]));
- $server->close($frame->fd);
- return;
- }
- } else {
- $server->push($frame->fd, json_encode([
- 'event' => 'show_msg',
- 'data' => '错误的请求!',
- 'close' => true
- ]));
- $server->close($frame->fd);
- return;
- }
- $o = new $data['c']([$server, $frame, $this->swooleCommon]); // 新建对象
- if (!method_exists($o, $data['a'])) {
- $server->push($frame->fd, json_encode([
- 'event' => 'show_msg',
- 'data' => '访问的方法不存在!'
- ]));
- return;
- }
- if ($o->authCheck($data['a'])) {
- $data['data'] = $data['data'] ?? [];
- call_user_func_array([$o, $data['a']], [$data['data']]); //调用对象$o($c)里的方法$a
- }
- }
- /**
- * 链接关闭
- */
- public function onClose($server, $fd, $reactorId)
- {
- // 解除fd绑定并修改用户状态
- $uid = $this->swooleCommon->getUidByFd($fd);
- if (!$this->swooleCommon->unbindUid($fd, $uid)) {
- /**
- * 用户断开1分钟后才设置为离线状态
- * Timer::after 不同于sleep,不会阻塞
- */
- Timer::after(60000, function () use ($uid) {
- if (!$this->swooleCommon->getFdByUid($uid)) {
- Db::name('fastim_user')->where('id', $uid)->update([
- 'status' => 0
- ]);
- $this->swooleCommon->radioStatusMessage($uid, 0);
- }
- });
- }
- }
- public function option(array $option)
- {
- if (!empty($option)) {
- $this->swoole->set($this->checkOptions($option));
- }
- // 注册回调
- foreach ($this->event as $event) {
- if (method_exists($this, 'on' . $event)) {
- $this->swoole->on($event, [$this, 'on' . $event]);
- }
- }
- // 实例化swooleCommon类
- $this->swooleCommon = new \addons\fastim\library\swoole\Common($option['max_connections'], $this->swoole);
- }
- protected function checkOptions(array $options)
- {
- if (class_exists(Helper::class)) {
- $constOptions = Helper::GLOBAL_OPTIONS + Helper::SERVER_OPTIONS + Helper::PORT_OPTIONS + Helper::HELPER_OPTIONS;
- foreach ($options as $k => $v) {
- if (!array_key_exists(strtolower($k), $constOptions)) {
- unset($options[$k]);
- }
- }
- }
- return $options;
- }
- public function setMonitor($interval = 2, $path = [])
- {
- $this->monitor['interval'] = $interval;
- $this->monitor['path'] = (array)$path;
- }
- /**
- * 文件监控
- *
- * @param $server
- */
- public function monitor($server)
- {
- if ($this->monitor['path']) {
- $server->tick($this->monitor['interval'], function () use ($server) {
- foreach ($this->monitor['path'] as $path) {
- $dir = new \RecursiveDirectoryIterator($path);
- $iterator = new \RecursiveIteratorIterator($dir);
- foreach ($iterator as $file) {
- if (pathinfo($file, PATHINFO_EXTENSION) != 'php') {
- continue;
- }
- if ($this->lastMtime < $file->getMTime()) {
- $this->lastMtime = $file->getMTime();
- echo '[update]' . $file . " reload...\n";
- $server->reload();
- return;
- }
- }
- }
- });
- }
- }
- /**
- * 魔术方法 有不存在的操作的时候执行
- * @access public
- * @param string $method 方法名
- * @param array $args 参数
- * @return mixed
- */
- public function __call($method, $args)
- {
- call_user_func_array([$this->swoole, $method], $args);
- }
- }
|