使用Easyswoole 搭建简单的Websoket服务

news2024/11/25 11:56:28

步骤1   修改配置文件MAIN_SERVER.SERVER_TYPEEASYSWOOLE_WEB_SOCKET_SERVER  

如dev.php

<?php
use EasySwoole\Log\LoggerInterface;

return [
    'SERVER_NAME'=>"EasySwoole",
    'MAIN_SERVER'=>[
        'LISTEN_ADDRESS'=>'0.0.0.0',
        'PORT'=>'19501',
        "SERVER_TYPE"=>EASYSWOOLE_WEB_SOCKET_SERVER,  //可选为 EASYSWOOLE_SERVER  EASYSWOOLE_WEB_SERVER   EASYSWOOLE_WEB_SOCKET_SERRVER 
        "SOCK_TYPE"=>SWOOLE_TCP,
        "RUN_MODEL"=>SWOOLE_PROCESS,
        "SETTING"=>[
            'worker_num'=>8,
            'reload_async'=>true,
            'max_wait_time'=>3,
            'package_max_length'=>1024*1024*1024,
            'max_connection'=>150000,
            'socket_buffer_size'=>1024*1024*1024
        ],
    ],
];

步骤2  EasySwooleEvent中mainServerCreate事件进行回调注册:

<?php


public static function mainServerCreate(\EasySwoole\EasySwoole\Swoole\EventRegister $register)
{

     $config = new \EasySwoole\Socket\Config();
     $config->setType($config::WEB_SOCKET);
     $config->setParser(WebSocketParser::class);
     $dispatcher = new Dispatcher($config);
     $config->setOnExceptionHandler(function (\Swoole\Server $server, \Throwable $throwable, string $raw, WebSocket $client, Response $response) {
           $response->setMessage('system error!');
           $response->setStatus($response::STATUS_RESPONSE_AND_CLOSE);
     });

      // 自定义握手
     /*$websocketEvent = new WebSocketEvent();
     $register->set(EventRegister::onHandShake, function (\Swoole\Http\Request $request, \Swoole\Http\Response $response) use ($websocketEvent) {
         $websocketEvent->onHandShake($request, $response);
     });*/

      $register->set($register::onMessage, function (\Swoole\Websocket\Server $server, \Swoole\Websocket\Frame $frame) use ($dispatcher) {
           $dispatcher->dispatch($server, $frame->data, $frame);
       });

    //注册服务时间
    $register->add(EventRegister::onOpen,[WebSocketEvents::class,'onOpen']);
    $register->add(EventRegister::onClose,[WebSocketEvents::class,'onClose']);
    
}

步骤3  WebSocketEvents.php

<?php

namespace App\WebSocket;

use EasySwoole\MysqliPool\Mysql;
use EasySwoole\Mysqli\Config as MysqlConfig;
use EasySwoole\RedisPool\Config as RedisConfig;
use EasySwoole\RedisPool\Redis;
use EasySwoole\FastCache\Cache;
use SebastianBergmann\CodeCoverage\Report\PHP;

class WebSocketEvents {
    //监听ws连接事件
    public static function onOpen(\swoole_websocket_server $server, \swoole_http_request $request) {
        echo $request->fd . '链接成功' . PHP_EOL;
        //return true;
    }

    //监听ws关闭事件
    public static function onClose(\swoole_server $server, int $fd, int $reactorId) {
        //echo $reactorId . ' -- ' . $fd . ' websocket 关闭' . PHP_EOL;
        //return true;
    }

    /**
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    public function onHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response) {
        /** 此处自定义握手规则 返回 false 时中止握手 */
        if (!$this->customHandShake($request, $response)) {
            $response->end();
            return false;
        }

        /** 此处是  RFC规范中的WebSocket握手验证过程 必须执行 否则无法正确握手 */
        if ($this->secWebsocketAccept($request, $response)) {
            $response->end();
            return true;
        }

        $response->end();
        return false;
    }

    /**
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    protected function customHandShake(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool {
        /**
         * 这里可以通过 http request 获取到相应的数据
         * 进行自定义验证后即可
         * (注) 浏览器中 JavaScript 并不支持自定义握手请求头 只能选择别的方式 如get参数
         */
        $headers = $request->header;
        $cookie = $request->cookie;

        // if (如果不满足我某些自定义的需求条件,返回false,握手失败) {
        //    return false;
        // }
        return true;
    }

    /**
     * RFC规范中的WebSocket握手验证过程
     * 以下内容必须强制使用
     *
     * @param \Swoole\Http\Request $request
     * @param \Swoole\Http\Response $response
     * @return bool
     */
    protected function secWebsocketAccept(\Swoole\Http\Request $request, \Swoole\Http\Response $response): bool {
        // ws rfc 规范中约定的验证过程
        if (!isset($request->header['sec-websocket-key'])) {
            // 需要 Sec-WebSocket-Key 如果没有拒绝握手
            var_dump('shake fai1 3');
            return false;
        }
        if (0 === preg_match('#^[+/0-9A-Za-z]{21}[AQgw]==$#', $request->header['sec-websocket-key'])
            || 16 !== strlen(base64_decode($request->header['sec-websocket-key']))
        ) {
            //不接受握手
            var_dump('shake fai1 4');
            return false;
        }

        $key = base64_encode(sha1($request->header['sec-websocket-key'] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11',
            true));
        $headers = array(
            'Upgrade' => 'websocket',
            'Connection' => 'Upgrade',
            'Sec-WebSocket-Accept' => $key,
            'Sec-WebSocket-Version' => '13',
            'KeepAlive' => 'off',
        );

        if (isset($request->header['sec-websocket-protocol'])) {
            $headers['Sec-WebSocket-Protocol'] = $request->header['sec-websocket-protocol'];
        }

        // 发送验证后的header
        foreach ($headers as $key => $val) {
            $response->header($key, $val);
        }

        // 接受握手 还需要101状态码以切换状态
        $response->status(101);
        var_dump('shake success at fd :' . $request->fd);
        return true;
    }
}

步骤4  websocket解析器   

<?php

namespace App\WebSocket;

use EasySwoole\Socket\AbstractInterface\ParserInterface;
use EasySwoole\Socket\Client\WebSocket;
use EasySwoole\Socket\Bean\Caller;
use EasySwoole\Socket\Bean\Response;

/**
 * Class WebSocketParser
 *
 * 此类是自定义的 websocket 消息解析器
 * 此处使用的设计是使用 json string 作为消息格式
 * 当客户端消息到达服务端时,会调用 decode 方法进行消息解析
 * 会将 websocket 消息 转成具体的 Class -> Action 调用 并且将参数注入
 *
 * @package App\WebSocket
 */
class WebSocketParser implements ParserInterface {
    /**
     * decode
     * @param string $raw 客户端原始消息
     * @param WebSocket $client WebSocket Client 对象
     * @return Caller         Socket  调用对象
     */
    public function decode($raw, $client): ?Caller {
        // new 调用者对象
        $caller = new Caller();
        // 解析 客户端原始消息
        $data = json_decode($raw, true);
        if (!is_array($data)) {
            echo "decode message error1111! \n";
            return null;
        }
        /**
         * 设置被调用的类 这里会将ws消息中的 class 参数解析为具体想访问的控制器
         * 如果更喜欢 event 方式 可以自定义 event 和具体的类的 map 即可
         * 注 目前 easyswoole 3.0.4 版本及以下 不支持直接传递 class string 可以通过这种方式
         */
        $class = '\\App\\WebSocket\\WSController\\' . ucfirst($data['class'] ?? 'Index');
        $caller->setControllerClass($class);
        $action = $data['action'];
        $caller->setAction($action);
        // 检查是否存在args
        $args = isset($data['params']) && !empty($data['params']) ? $data['params'] : [];

        // 设置被调用的Args
        $caller->setArgs($args ?? []);
        return $caller;
    }

    /**
     * encode
     * @param Response $response Socket Response 对象
     * @param WebSocket $client WebSocket Client 对象
     * @return string             发送给客户端的消息
     */
    public function encode(Response $response, $client): ?string {
        /**
         * 这里返回响应给客户端的信息
         * 这里应当只做统一的encode操作 具体的状态等应当由 Controller处理
         */
        return $response->getMessage();
    }

}

步骤五 新增Websoket 控制器   Index.php

App\WebSocket\WSController

<?php
/*
 * ws控制器测试
 **/

namespace App\WebSocket\WSController;

use EasySwoole\EasySwoole\ServerManager;
use EasySwoole\Socket\AbstractInterface\Controller;

class Index extends Controller {

    public function index() {
        $client = $this->caller()->getClient();
        $server = ServerManager::getInstance()->getSwooleServer();
        $post_info = $this->caller()->getArgs();
        if ($server->getClientInfo($client->getFd())) {
            $server->push($client->getFd(), json_encode(['data' => 'success']));
        }
    }
    
    public function index1(){
        var_dump('这里是index1');
        
    }

}

步骤六  连接websoket  进行测试  

前端html 代码如下:

<!DOCTYPE html>
    <html>
        <head>
            <title>测试</title>
        </head>
        <body>
            <script type="text/javascript">
                ws = new WebSocket("ws:127.0.0.1:19501") 
                    
                ws.open = function () {
                    alert('success')
                    
                    str = '{"class":"Index1","action":"index1","params":[]}'
                    console.log(str)
                    ws.send(str)
                }

                ws.onmessage = function (evt) {
                    alert(evt.data)
                }

                ws.onclose = function (evt) {
                    console.log('colose')
                }
            </script>
        </body>
    </html>
</html>

注: 其中发送的数据   JSON字符串    {"class":"Index1","action":"index1","params":[]}

class 表示请求到对应的Websoket控制器  

action 标识请求到对应的Websoket方法

params 为发送的数据,这里必须数组  若为其他,可能接收不到!

结果如图:

 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.coloradmin.cn/o/102120.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

新鲜事儿!只有AI作品的电影节;酷~AI纹身设计师;ChatGPT的接生婆RLHF;Wayve自动驾驶模型MILE… | ShowMeAI资讯日报

&#x1f440;日报合辑 | &#x1f3a1;AI应用与工具大全 | &#x1f514;公众号资料下载 | &#x1f369;韩信子 &#x1f4e2; 『AI Film Festival』只接受AI作品的电影节&#xff0c;一万美元奖金花落谁家&#xff1f; https://aiff.runwayml.com/ Runway ML公司12月7日推…

前端开发--JavaScript基础知识点大全

前端开发--JavaScript基础知识点大全JavaScript 基础1、JavaScript介绍2、变量3、数据类型4、类型转换5、 运算符6、语句6.1 表达式和语句6.2 分支语句6.3 循环语句7、数组8、函数9、对象10、更多JavaScript 基础 1、JavaScript介绍 JavaScript是一种运行在客户端&#xff08…

一个桌面弹幕软件,用来记单词刚刚好

弹幕单词是一款桌面弹幕软件&#xff0c;它可以把词条以弹幕的形式呈现在桌面上&#xff0c;不管这个词条是英语单词还是中文成语&#xff0c;只要结合了相关资料&#xff0c;就非常方便的使用鼠标右键查看此弹幕的相关资料&#xff0c;下面我们来看看这个软件如何使用吧 快速入…

大学生家乡主题甘肃介绍网页代码 dreamweaver网页设计作业制作 WEB静态网页作业模板 dw个人网页作业成品

家乡旅游景点网页作业制作 网页代码运用了DIV盒子的使用方法&#xff0c;如盒子的嵌套、浮动、margin、border、background等属性的使用&#xff0c;外部大盒子设定居中&#xff0c;内部左中右布局&#xff0c;下方横向浮动排列&#xff0c;大学学习的前端知识点和布局方式都有…

前端vue面试题集锦1

Vue.extend 作用和原理 官方解释&#xff1a;Vue.extend 使用基础 Vue 构造器&#xff0c;创建一个“子类”。参数是一个包含组件选项的对象。 其实就是一个子类构造器 是 Vue 组件的核心 api 实现思路就是使用原型继承的方法返回了 Vue 的子类 并且利用 mergeOptions 把传入…

使用Mock技术帮助提升测试效率的小tips,你知道几个?

Mock是什么&#xff1f; 我给你描述一下这几个场景&#xff0c;你看看是不是熟悉的呢&#xff1f; 测试一个业务模块&#xff0c;这个业务模块中有几个接口对接到了第三方&#xff0c;因为第三方延期交付&#xff0c;导致项目延期&#xff0c;我又背锅了 我想测试前端页面字体…

作为测试人员,你需要掌握哪些,常用软件测试工具?

前言 作为软件测试人员&#xff0c;你最常用到的测试工具都有哪些呢&#xff1f;本文总结了测试过程中常用的工具&#xff0c;希望对从业人员日常工作有所助益&#xff01; 包含工具内容 测试管理工具 连接服务器 抓包工具 接口测试工具 app自动化工具 性能测试工具 持续集成…

[附源码]计算机毕业设计Node.js橙光公司网站设计论文(程序+LW)

项目运行 环境配置&#xff1a; Node.js最新版 Vscode Mysql5.7 HBuilderXNavicat11Vue。 项目技术&#xff1a; Express框架 Node.js Vue 等等组成&#xff0c;B/S模式 Vscode管理前后端分离等等。 环境需要 1.运行环境&#xff1a;最好是Nodejs最新版&#xff0c;我…

nodejs+vue067高校 校园疫情防控系统

录 1 绪论 1 1.1 研究背景 1 1.2 研究现状 1 1.3 研究内容 2 2 系统关键技术 3 2.3 MYSQL数据库 4 2.4 B/S结构 4 3 系统分析 5 3.1 可行性分析 5 3.1.1 技术可行性 5 3.1.2经济可行性 5 3.1.3操作可行性 5 3.2 系统性能分析 5 3.3 …

Windows10下用Vscode配置OpenCV (解包即用版)

Windows10下用Vscode配置OpenCV (解包即用版) 维基百科&#xff1a; OpenCV的全称是Open Source Computer Vision Library&#xff0c;是一个跨平台的计算机视觉库。OpenCV是由英特尔公司发起并参与开发&#xff0c;以BSD许可证授权发行&#xff0c;可以在商业和研究领域中免费…

三、详细功能

详细功能 1.去除不同数据库间重复的文献&#xff1a;Reference->Find Duplicates &#xff08;1&#xff09;找到4篇重复的文章&#xff1a;Keep this record可以一篇篇的保留、删除 &#xff08;2&#xff09;按Cancel再按ctrl找到所有重复&#xff1a; &#xff08;3&am…

长达22643字的博客:自定义游戏三子棋超详细解说手把手式教学,人机大战,PVP玩家对战功能制作详解

&#x1f992;这个游戏相信大家都听过&#xff0c;三子棋&#xff0c;不就是井字棋吗&#xff1f;那么今天我们就来亲手制作一个三子棋游戏&#xff0c;实现人机“智能”大战以及玩家PVP对战。 &#x1f412;三子棋的基本功能构思 &#x1f992;面对一个较大的游戏程序我们要做…

(附源码)Springboot社区疫情防控管理系统 毕业设计 164621

Springboot 社区疫情防控管理系统 摘 要 信息化社会内需要与之针对性的信息获取途径&#xff0c;但是途径的扩展基本上为人们所努力的方向&#xff0c;由于站在的角度存在偏差&#xff0c;人们经常能够获得不同类型信息&#xff0c;这也是技术最为难以攻克的课题。针对社区疫情…

校园生活互助网站

开发工具(eclipse/idea/vscode等)&#xff1a; 数据库(sqlite/mysql/sqlserver等)&#xff1a; 功能模块(请用文字描述&#xff0c;至少200字)&#xff1a; 体智慧校园生活互助系统主要实现以下功能&#xff1a; (一)网站前台&#xff1a;关于我们、联系我们、公告信息、求助信…

DataGear 添加 Http 数据集是请求头 JSON 的格式说明

背景 玩了一下 DataGear &#xff0c;添加了一个 Http 接口的数据集&#xff0c;由于目标接口请求必须包含 Token 信息&#xff0c;所以按照配置提示添加 JSON 格式的头信息时&#xff0c;想象成了 JSON 对象&#xff0c;结果预览请求始终报错。 DataGear 的后台异常信息&…

【JUC】JMM内存模型

JMM是什么 JMM是Java内存模型&#xff0c;也就是Java Memory Model&#xff0c;简称JMM&#xff0c;本身是一种抽象的概念,并不真实存在&#xff0c;它描述的是一组规则或规范&#xff0c;通过这组规范定义了程序中各个变量&#xff08;包括实例字段&#xff0c;静态字段和构成…

Graphviz 排版思路 Graphviz 如何控制节点位置

有个画图需求是需要生成特定排版的关系图。 graphviz 对于一般情况都是自动排的&#xff0c;有时候会抽风&#xff0c;比如这种情况&#xff1a; 而我们想要这样&#xff1a; 对初学者提一下 &#xff0c;如何实现 3 2 5 &#xff0c;1467 从左到右排列呢&#xff1f;待会会顺…

HTML小游戏18 —— html5版街头霸王游戏(附完整源码)

&#x1f482; 网站推荐:【神级源码资源网】【摸鱼小游戏】&#x1f91f; 前端学习课程&#xff1a;&#x1f449;【28个案例趣学前端】【400个JS面试题】&#x1f485; 想寻找共同学习交流、摸鱼划水的小伙伴&#xff0c;请点击【摸鱼学习交流群】&#x1f4ac; 免费且实用的计…

【Three.js】Three.js快速上手教程

1. Three.js简介 官网对 Three.js 的介绍非常简单&#xff1a;“Javascript 3D library”。 即&#xff1a;three.js是JavaScript编写的WebGL第三方库。提供了非常多的3D显示功能&#xff0c;是一个常见的web 3D库。 相关概念解释&#xff1a;three.js > webGL > openG…

QT QTabWidget 控件 使用详解

本文详细的介绍了QTabWidget控件的各种操作&#xff0c;例如&#xff1a;新建界面、设置页面名字、设置提示信息、设置页面激活、设置标题栏位置、设置页面关闭按钮、设置页面关闭按钮、获取页面下标、获取页面总数、清空所有页面、删除某个页面、设置拖拽移动、设置显示页面、…