think-swoole使用教程

核心思想是swoole只是作为一个消息转发器,业务逻辑还是通过接口来实现,发送消息也是使用接口,客户端websocket只负责创建和监听接受消息即可。

环境

  1. centos8
  2. PHP7.4
  3. thinkphp6.0.10
  4. think-swoole4.0.6

开发过程

  1. 安装think-swoole扩展

  2. 为了方便我们安装think-view扩展

  3. 配置swoole.php文件

    1. server.host 服务器IP
    2. server.port 服务器端口
    3. server.options.daemonize 是否进程
    4. websocket.enable 打开websocket
    5. websocket.handle 自己接管或者使用默认(默认的会给我们发送socket消息,不理会即可)
    6. websocket.subscribe 创建事件订阅,我这里的文件名是WebSocketEvent(也可以使用监听,只不过需要多个文件)
    7. 因为是多进程,我们需要共享变量,可以用MySQL、redis等,我们这里使用swoole的共享内容Table,因为同一个用户可能是多端登录,我们创建俩个Table,一个是用户映射fd,一个是fd映射用户,Table的映射是一对一的,但是一个用户可能有多个fd,所以用户映射fd的Table的值使用逗号分隔的多个值,例如用户1->fd1,fd2
    8. 配置tables俩个table,分别是m2fd、fd2m,thinkphp实现的Table如何使用请自己看代码
        'tables'     => ['m2fd' => ['size' => 102400,'columns' => [['name' => 'fd', 'type' => \Swoole\Table::TYPE_STRING, 'size' => 50]]],'fd2m' => ['size' => 102400,'columns' => [['name' => 'member_id', 'type' => \Swoole\Table::TYPE_INT]]],],
  4. 通过订阅实现websocket逻辑

  5. 把我们需要使用的类通过构造函数依赖注入,方便使用

  6. 我们需要WebSocket类实现通信逻辑,Table类实现用户fd映射

  7. 如果我们使用了type为11的绑定方式,则订阅open事件,发送给客户端

  8. message事件方法体留空或者不写即可,我们使用接口来实现逻辑

  9. close事件移除用户和fd的映射关系

  10. 我们定义一个事件,用于接口触发,从而实现发送消息逻辑,事件名称叫做ApiEvent,代码如下

<?php
declare (strict_types = 1);namespace app\subscribe;use app\Request;
use Swoole\Server;
use think\swoole\Table;
use think\swoole\Websocket;class WebSocketEvent
{private $websocket = null;private $m2fd = null;private $fd2m = null;public function __construct(Websocket $websocket, Table $table){$this->websocket = $websocket;$this->m2fd = $table->get('m2fd');$this->fd2m = $table->get('fd2m');}// 这里之所以注入一个请求,是因为如果我们不用type=11这种方式绑定,则可以通过new WebSocket的时候把用户ID传递过来,然后直接实现绑定public function onOpen(Request $request){$currentFd = $this->websocket->getSender();$data = ['type' => 11,'fd' => $currentFd];$this->websocket->push(json_encode($data));}public function onClose(){$currentFd = $this->websocket->getSender();// 通过fd找到用户ID$memberId = $this->fd2m->get((string)$currentFd, 'member_id');// 如果没有找到映射,就说明没有绑定过,就什么不做,找到的话就解除绑定if ($memberId) {$this->fd2m->del((string)$currentFd);// 根据用户ID找到映射的所有fd,然后把存在的当前fd移除掉$fds = $this->m2fd->get((string)$memberId, 'fd');if ($fds) {$fdArray = explode(',', $fds);$key = array_search($currentFd, $fdArray);unset($fdArray[$key]);if ($fdArray) {$resFds = implode(',', $fdArray);$this->m2fd->set((string)$memberId, $resFds);} else {$this->m2fd->del((string)$memberId);}}}}public function onApiEvent($data){// $data是接口传递过来的参数,如果是11则实现绑定,是5就转发给from_id和to_idif ($data['type'] == 11) {// m2fd、fd2m俩个Table的映射$this->fd2m->set((string)$data['fd'], ['member_id' => $data['member_id']]);// 先查找该用户ID是否已经绑定过其它fd了$fds = $this->m2fd->get((string)$data['member_id'], 'fd');if (!$fds) {$this->m2fd->set((string)$data['member_id'], ['fd' => $data['fd']]);} else {// 看看fd是否在已经映射的fd中,如果在就什么都不做,如果不在就追加到后面$fdArray = explode(',', $fds);if (!in_array($data['fd'], $fdArray)) {$this->m2fd->set((string)$data['member_id'], ['fd' => $fds . ',' . $data['fd']]);}}}if ($data['type'] == 1) {// 根据from_id和to_id俩个用户ID找到对应的fd,然后发送消息$fromFds = $this->m2fd->get((string)$data['from_id'], 'fd');$toFds = $this->m2fd->get((string)$data['to_id'], 'fd');$fromFdArray = $toFdArray = [];if ($fromFds) {$fromFdArray = explode(',', $fromFds);}if ($toFds) {$toFdArray = explode(',', $toFds);}// 合并所有发送者fd和接受者fd,之所以发送给发送者,一方面是简化前端工作,前端只需要接受websocket消息即可,另一方面,多端的话其它端可以可以即时看到聊天记录$allFdArray = array_unique(array_merge($fromFdArray, $toFdArray));// 发送消息$this->websocket->to($allFdArray)->push(json_encode($data));}}
}
  1. 接口实现代码如下
<?php
namespace app\controller;class Index
{// 为了演示方便我们不使用路由了,使用控制器方法的方式访问public function index(){// 聊天页面return view();}// 如果使用type=1的绑定方式就要,否则这个可以不要public function bindMember(){// 用户ID本来是要从登录状态中获取的,这里我们是模拟演示,就让前端传$params = request()->only(['member_id', 'fd']);// 触发ApiEvent事件,组装数据type=11、member_id、fd$data = $params;$data['type'] = 11;event('swoole.websocket.ApiEvent', $data);// 只要不抛异常就是绑定成功了$res = ['code' => 1,'msg' => '绑定成功'];return json($res);}// 我们只写一个发送文字消息的例子public function sendMessage(){// 其它需要的字段我们就不写了,自己实现即可,持久化到数据库逻辑也不写了,只是写消息发送$params = request()->only(['from_id', 'to_id', 'content']);$data = $params;$data['type'] = 1;event('swoole.websocket.ApiEvent', $data);// 不抛异常就是成功了$res = ['code' => 1,'msg' => '发送消息成功'];return json($res);}
}
  1. 聊天页面代码
<!doctype html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport"content="width=device-width, user-scalable=no, initial-scale=1.0, maximum-scale=1.0, minimum-scale=1.0"><meta http-equiv="X-UA-Compatible" content="ie=edge"><title>聊天页面</title><style>#chat {height: 400px;width: 400px;background: gray;}</style>
</head>
<body>
<div id="chat"></div>
<button id="connect">链接websocket服务端</button>
<input type="text" id="content" value="内容" placeholder="聊天内容">
<input type="text" id="to" placeholder="目标对象" value="1">
<input type="text" id="from" placeholder="发送对象" value="1">
<button id="submit">发送</button><script src="https://cdn.bootcdn.net/ajax/libs/jquery/3.6.0/jquery.min.js"></script>
<script>var ws = null;// 链接websocket$("#connect").click(function () {// TODO 改成自己的域名即可ws = new WebSocket("ws://swoole.dreamphp.com.cn:8282");ws.onmessage = function (res) {var data = JSON.parse(res.data);//  如果没有接管Handler,则消息格式就跟我们的不一样,可能会报错,不用理会的if (data.type == 11) {$.ajax({url: "{:url('index/bindMember')}",type: "post",data: {member_id: $("#from").val(), fd: data.fd},dataType: "json",success: function (res) {console.log(res);}});}if (data.type == 1) {$("#chat").append("用户" + data.from_id + ":" + data.content + "<br>");}};return false;});// 发送内容$("#submit").click(function () {var toId = $("#to").val();var fromId = $("#from").val();var content = $("#content").val();$.ajax({url: "{:url('index/sendMessage')}",type: "post",data: {from_id: fromId, to_id: toId, content: content},dataType: "json",success: function (res) {console.log(res)}});return false;});
</script>
</body>
</html>

type说明(type为1可以去掉,可以换成new websocket的时候就直接绑定)

type 说明 额外说明
11 通知用户要绑定了 fd
1 聊天消息 消息类型详细说明

消息类型详细说明

【2022/1/12】think-swoole使用教程相关推荐

  1. Swoole入门指南:PHP7安装Swoole详细教程(一) 1

    这里不在使用apache做为web server.该用nginx + php-fpm,性能更强大,配置更方便.并且为了跟上php的步伐,也使用了比较新的php版本 [x] centos7 [x] ph ...

  2. 2022年12月编程语言排行榜,数据来了!

    2022年迎来了最后一个月,我们可以看到,在这一年中编程语言起起伏伏,有的语言始终炙手可热,而有的语言却逐渐"没落"- 日前,全球知名TIOBE编程语言社区发布了12月编程语言排行 ...

  3. 2022年12月大学英语B统考题库试题

    2022年第三次统考时间预计在12月举行完毕,2022年12月奥鹏网络教育统考已有不少院校相继出通知,以下节选了部分高校网络教育统考时间节点以表格的形式展示给大家,例如:2022年西南大学网络教育统考 ...

  4. 第14届蓝桥杯STEMA测评真题剖析-2022年12月18日Scratch编程初中级组

    [导读]:超平老师的<Scratch蓝桥杯真题解析100讲>已经全部完成,后续会不定期解读蓝桥杯真题,这是Scratch蓝桥杯真题解析第109讲. 蓝桥杯选拔赛现已更名为STEMA,即ST ...

  5. 20172319 2018.10.12《Java程序设计教程》第6周课堂实践(补写博客)

    20172319 2018.10.12 <Java程序设计教程>第6周课堂测验 课程:<程序设计与数据结构> 班级:1723 学生:唐才铭 学号:20172319 指导老师:王 ...

  6. windows下php swoole扩展,Windows 下安装 swoole 图文教程(php)

    Windows 下安装 swoole 具体步骤: Swoole,原本不支持在Windows下安装的,所以我们要安装Cygwin来使用.在安装Cygwin下遇到了很多坑,百度经验上的文档不是很全,所以我 ...

  7. 【记录】ChatGPT|注册流程、使用技巧与应用推荐(更新至2022年12月14日)

      昨天,2022年12月13日,在下午和晚上,ChatGPT 就开始因为请求过多而写到一半就崩溃,出现network error,可见它的关注度确实是越来越可观了.   正好最近世界杯,有博客活动, ...

  8. Premiere Pro 2022带来离线语音转文本教程

    Premiere Pro 2022大版本更新,在去年的的10月份更新也带来了不少实用功能.今日小编就为大家带来 Premiere Pro 2022 离线语音转文本教程.感兴趣的小伙伴们欢迎收藏! Ad ...

  9. 汇总2022年12月托福toefl考试/解析答案为你助力

    我喜欢车,2022年12月托福toefl考试/解析答案3069519625[汇总]就因为车的漂亮与先进.车的颜色多种多样,开得又快,所以我从小便喜欢车12月3日托福.12月4日托福.12月5日托福.1 ...

最新文章

  1. 【转】 Android中退出程序的提示框
  2. python源代码最多的学习网站_史上最全Python学习资料大合集分享
  3. Adb shell命令打电话测试4G
  4. 有感而发,恍然大悟。
  5. nfc标签 方案 防伪_NFC技术解读及ST NFC产品与方案
  6. java映射的概念_Java 反射 概念理解
  7. poj 4468Spy(kmp算法)
  8. C语言 · 数的读法
  9. UVa 10570 - Meeting with Aliens
  10. C\C++获取当前路径
  11. java pixel data_java - JavaFX PixelWriter性能低下
  12. FFmpeg总结(十三)用ffmpeg基于nginx实现直播功能,不用第三方SDK,自研推流拉流
  13. 人对光波的三种特性_面试题:常用塑胶材料的特性及用途,你能列出几个?
  14. windows win32 API大全
  15. 面试官常问 webpack 面试题
  16. 让摄像头脱离线的束缚:使用手机充当电脑摄像头
  17. 微软、IBM对垒大数据
  18. 1093:计算多项式的值
  19. vue中xlsx导出多个sheet页
  20. win7启动无法自动修复此计算机,win7系统启动修复_win7系统中启动修复无法自动修复此计算机情况的三种解决方法介绍_win7双系统启动修复...

热门文章

  1. python切比雪夫滤波器_[Matlab]切比雪夫Ⅰ型滤波器设计:低通、高通、带通和带阻...
  2. 喜讯 | 创新引领,数据赋能,数说故事再度斩获金鼠标数字营销大赛双项大奖
  3. phpspreadsheet常用设置项
  4. 2021-02-05仅供自己参考:多态使用
  5. 【解决方案 二十三】带单位的数值转大写人民币金额
  6. Try to increase the 20000ms adb execution timeout represented by 'uiautomator2ServerInstallTimeout'
  7. 网站DDOS攻击防护实战老男孩经验心得分享 【转】
  8. Android Bitmap 缩放
  9. canvas图形缩放功能
  10. ORB-SLAM3中的词袋模型BoW