- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
导读
本文以 CRMEB 仓库中内置的 Workerman 官方 README 为核心骨架,系统讲解 Workerman 异步事件驱动框架的环境要求、安装方式、六大服务搭建(WebSocket/HTTP/WebServer/TCP/SSL/自定义协议)、Timer 定时器、AsyncTcpConnection 异步连接以及 ReactPHP 生态集成,并结合 CRMEB 的think workerman命令、config/workerman.php配置与crmeb/services/workerman源码,说明后台管理员消息通知、H5 客服聊天、新订单弹窗等长连接场景的真实落地方式。读完你将掌握 Workerman 的完整入门到进阶用法,并能看懂 CRMEB 长连接服务的进程结构与消息分发链路。
一、Workerman 是什么:异步事件驱动的 PHP 网络框架
Workerman 是一个纯 PHP 实现的异步事件驱动网络框架,用于快速构建高性能、可扩展的网络应用。它不依赖 PHP-FPM/Nginx,而是自己常驻内存监听端口、处理并发连接,因此天然适合聊天、实时推送、在线客服、物联网网关等长连接场景。
在 CRMEB 项目中,Workerman 承担三类核心长连接职责(见 config/workerman.php 顶部注释):
- channel(内部通讯):系统程序收到消息后,通过此端口推送给对应客户/客服,以及后台新订单弹窗提醒;
- admin(后台通知):新订单、新退款单发送消息给程序,后台消息通知;
- chat(客服聊天):客户或客服发送消息给程序,客服消息通知。
从依赖角度,当前仓库锁定的是 Workerman 3.5.19 版本(见 Worker.php 中的const VERSION = '3.5.19')。其内部结构分为四层:事件循环层(Workerman\Events,支持 Select/libevent/Event/Swoole/React 等)、连接层(Workerman\Connection)、协议层(Workerman\Protocols,内置 HTTP/WebSocket/Text/Frame)以及核心的 Worker.php 进程容器。
核心特性
- 支持 HTTP、WebSocket、SSL 及任意自定义协议;
- 支持多进程部署(
count属性设置进程数),多进程间通过 Channel 组件通信; - 支持 libevent、HHVM、ReactPHP 等事件循环扩展;
- 完全常驻内存,无 PHP 请求生命周期开销。
二、环境要求与安装
Workerman 官方 README 明确给出的运行前提如下:
| 项目 | 要求 |
|---|---|
| PHP 版本 | PHP 5.3 或更高 |
| 操作系统 | POSIX 兼容系统(Linux、macOS、BSD) |
| PHP 扩展 | POSIX 与 PCNTL 扩展 |
需要说明的是,这里“PHP 5.3+”是 Workerman 库自身的最低要求;而 CRMEB v5 后端运行环境要求PHP 7.1–7.4(见 crmeb/README.md),实际部署时以 CRMEB 的要求为准。
安装方式(Composer):
composer require workerman/workerman当前仓库中该依赖已通过 Composer 安装至crmeb/vendor/workerman/workerman,其 composer.json 声明了 PSR-4 自动加载(Workerman\→./),并建议安装ext-event以获得更好的性能。若要使用 ReactPHP 生态,还需按需安装react/mysql、clue/redis-react、react/dns、react/http-client、react/zmq、react/stomp等包。
Windows 并非推荐运行环境:
php think workerman start --d等守护进程模式依赖 POSIX 信号机制,在 Windows 下需要分三步分别启动内部通讯、admin、chat 三个服务(详见 crmeb/README.md)。
三、五种基础服务搭建实战
Workerman 统一以「协议 URI + 回调」的方式组织代码:new Worker("协议://监听地址:端口"),然后给实例挂上onConnect/onMessage/onClose等回调,最后调用Worker::runAll()启动。以下示例均可在独立start.php中直接运行。
1. WebSocket 服务器
<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // 创建 Websocket 服务器 $ws_worker = new Worker("websocket://0.0.0.0:2346"); // 4 个进程 $ws_worker->count = 4; // 新连接到来时触发 $ws_worker->onConnect = function($connection) { echo "New connection\n"; }; // 收到数据时触发 $ws_worker->onMessage = function($connection, $data) { // 给客户端回送 hello $data $connection->send('hello ' . $data); }; // 连接关闭时触发 $ws_worker->onClose = function($connection) { echo "Connection closed\n"; }; // 运行 Worker Worker::runAll();要点解读:
count决定启动的子进程数,多进程可显著提升并发吞吐,但 CRMEB 中 admin/chat 均配置为1(见下文),因为需要依靠单进程内存中的$user映射表维护“用户 ID → 连接”关系;- 三个回调各自独立,对应 Worker.php 中
onWorkerStart、onConnect、onMessage、onClose、onError、onBufferFull、onBufferDrain等公开属性(回调)。
2. HTTP 服务器
Workerman 的 HTTP 协议处理器会解析请求并填充 PHP 超全局变量,因此在onMessage中可以直接使用$_GET、$_POST、$_COOKIE、$_SESSION、$_SERVER、$_FILES:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // #### http worker #### $http_worker = new Worker("http://0.0.0.0:2345"); // 4 个进程 $http_worker->count = 4; // 收到数据时触发 $http_worker->onMessage = function($connection, $data) { var_dump($_GET, $_POST, $_COOKIE, $_SESSION, $_SERVER, $_FILES); // 给客户端回送数据 $connection->send("hello world \n"); }; // 运行所有 worker Worker::runAll();从源码看,Http.php 支持GET/POST/PUT/DELETE/HEAD/OPTIONS六种方法,input()负责判断请求是否完整(依据\r\n\r\n与Content-Length计算包长度),并校验包大小上限maxPackageSize,不合法请求直接返回400 Bad Request。
3. WebServer(静态网站服务器)
Workerman\WebServer是 Worker 的子类,基于 HTTP 协议封装了虚拟主机与静态文件服务能力:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\WebServer; use Workerman\Worker; // WebServer $web = new WebServer("http://0.0.0.0:80"); // 4 个进程 $web->count = 4; // 设置域名对应的站点根目录 $web->addRoot('www.your_domain.com', '/your/path/Web'); $web->addRoot('www.another_domain.com', '/another/path/Web'); // 运行所有 worker Worker::runAll();从 WebServer.php 的实现可见:addRoot()将“域名 → 站点根目录”写入$serverRoot映射;进程启动时若映射为空会直接报错退出(server root not set),同时初始化mime.types表(见Protocols/Http/mime.types)。
4. TCP 服务器
最底层的 TCP 服务器是理解 Workerman 数据流的起点——所有上层协议(HTTP/WebSocket/Text)最终都是对 TCP 字节流的input/decode/encode拆包封包:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // #### 创建 socket 并监听 1234 端口 #### $tcp_worker = new Worker("tcp://0.0.0.0:1234"); // 4 个进程 $tcp_worker->count = 4; // 新连接到来时触发 $tcp_worker->onConnect = function($connection) { echo "New Connection\n"; }; // 收到数据时触发 $tcp_worker->onMessage = function($connection, $data) { $connection->send("hello $data \n"); }; // 连接关闭时触发 $tcp_worker->onClose = function($connection) { echo "Connection closed\n"; }; Worker::runAll();5. 开启 SSL(wss:// / https://)
通过传入 stream context 再切换传输层transport = 'ssl',即可将服务升级为加密通道:
<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // SSL context $context = array( 'ssl' => array( 'local_cert' => '/your/path/of/server.pem', 'local_pk' => '/your/path/of/server.key', 'verify_peer' => false, ) ); // 创建带 SSL context 的 Websocket 服务器 $ws_worker = new Worker("websocket://0.0.0.0:2346", $context); // 启用 SSL。WebSocket+SSL 即 Secure WebSocket(wss://) // https 等也采用类似方式 $ws_worker->transport = 'ssl'; $ws_worker->onMessage = function($connection, $data) { $connection->send('hello ' . $data); }; Worker::runAll();CRMEB 在 Workerman.php 命令 中完整复刻了这一模式:从系统配置读取证书文件路径(wssLocalCert/wssLocalpk),组装local_cert、local_pk、verify_peer => false的 SSL context,再按需设置transport = 'ssl'。当前仓库中wss_open被固定为0,即默认走明文 websocket,开启 WSS 需自行申请域名证书。
四、自定义协议:Text + "\n" 的完整范例
当内置协议不满足业务时,可以自定义协议。协议类只需实现三个静态方法,Workerman 会通过ProtocolInterface(见 Protocols/ProtocolInterface.php)调用它们:
input($recv_buffer):判断缓冲区中是否已有一个完整包,返回包长度;不足返回 0;decode($recv_buffer):把原始字节解析为业务数据;encode($data):把业务数据编码为发送字节流。
以下示例定义了一个「每包以\n结尾」的文本协议:
Protocols/MyTextProtocol.php
namespace Protocols; /** * 用户自定义协议 * 格式 Text+"\n" */ class MyTextProtocol { public static function input($recv_buffer) { // 查找 "\n" 首次出现的位置 $pos = strpos($recv_buffer, "\n"); // 尚未收到完整包,返回 0 表示无法计算包长度 if($pos === false) { return 0; } // 返回包长度 return $pos+1; } public static function decode($recv_buffer) { return trim($recv_buffer); } public static function encode($data) { return $data."\n"; } }在 Worker 中以MyTextProtocol://前缀注册:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; // #### MyTextProtocol worker #### $text_worker = new Worker("MyTextProtocol://0.0.0.0:5678"); $text_worker->onConnect = function($connection) { echo "New connection\n"; }; $text_worker->onMessage = function($connection, $data) { $connection->send("hello world \n"); }; $text_worker->onClose = function($connection) { echo "Connection closed\n"; }; // 运行所有 worker Worker::runAll();与之对照,内置的 Websocket.php 的input()就是逐字节解析帧头(FIN、掩码、opcode、长度),握手未完成时进入dealHandshake,并校验客户端帧必须带掩码(frame not masked直接断开);Frame.php 则以 4 字节大端长度前缀拆包。理解这三个方法,就掌握了 Workerman 一切协议的底层逻辑。
五、Timer 定时器:进程内毫秒级任务调度
Timer 是 Workerman 内置的轻量定时器,适合做心跳检测、轮询清理等进程内任务:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; use Workerman\Lib\Timer; $task = new Worker(); $task->onWorkerStart = function($task) { // 间隔 2.5 秒 $time_interval = 2.5; $timer_id = Timer::add($time_interval, function() { echo "Timer run\n"; } ); }; // 运行所有 worker Worker::runAll();源码层面(Lib/Timer.php):
Timer::add($time_interval, $func, $args = array(), $persistent = true),$persistent = false时仅执行一次(EV_TIMER_ONCE);- 未启用事件扩展时,基于
pcntl_alarm(1)+SIGALRM信号驱动,每次 tick 将到期任务执行后按原间隔重新挂入任务表; - 启用 libevent/Event 等扩展时,直接委托给事件循环的
EV_TIMER通道,精度更高; Timer::del($timer_id)与Timer::delAll()用于移除定时任务。
CRMEB 中的 Timer 应用
- 心跳与超时检测:在 WorkermanService.php 与 ChatService.php 中,
Timer::add(15, ...)每 15 秒扫描全部连接,若当前时间 - lastMessageTime > 12秒则判定掉线并close('timeout'),同时把在线状态写回数据库、广播给客服端。 - 在线状态刷新:
ChatService每 2 秒统计在线用户与在线客服的 uid 集合,将不在线的记录批量置为online = 0(见 ChatService.php)。 - CRMEB 定时任务命令:
php think timer start同样基于 Workerman 的 Worker 与 Timer 实现(见 crmeb/crmeb/command/Timer.php),在onWorkerStart中调用SystemCrontabServices::crontabCommandRun()执行平台配置的定时任务,支持-i 0.001自定义执行间隔,PID 文件写入runtime/timer.pid。
六、AsyncTcpConnection:异步客户端连接
AsyncTcpConnection让 Workerman 进程既能当服务器,也能当异步客户端,可主动连接外部 TCP/WebSocket 等服务:
require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; use Workerman\Connection\AsyncTcpConnection; $worker = new Worker(); $worker->onWorkerStart = function() { // 客户端使用 Websocket 协议 $ws_connection = new AsyncTcpConnection("ws://echo.websocket.org:80"); $ws_connection->onConnect = function($connection){ $connection->send('hello'); }; $ws_connection->onMessage = function($connection, $data){ echo "recv: $data\n"; }; $ws_connection->onError = function($connection, $code, $msg){ echo "error: $msg\n"; }; $ws_connection->onClose = function($connection){ echo "connection closed\n"; }; $ws_connection->connect(); }; Worker::runAll();从源码看,AsyncTcpConnection.php 继承自TcpConnection,默认transport = 'tcp'、_remotePort = 80,需要显式调用connect()建立连接,并支持tcp/ws/text/frame等协议前缀。这一能力是 CRMEB Channel 组件通信的底层基础之一。
七、ReactPHP 生态集成
Workerman 的事件循环与 ReactPHP 完全兼容,可直接在onWorkerStart中通过Worker::getEventLoop()获取事件循环并创建异步客户端。官方 README 给出了六个组合示例。
1. 异步 MySQL
composer require react/mysql<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; $worker = new Worker('tcp://0.0.0.0:6161'); $worker->onWorkerStart = function() { global $mysql; $loop = Worker::getEventLoop(); $mysql = new React\MySQL\Connection($loop, array( 'host' => '127.0.0.1', 'dbname' => 'dbname', 'user' => 'user', 'passwd' => 'passwd', )); $mysql->on('error', function($e){ echo $e; }); $mysql->connect(function ($e) { if($e) { echo $e; } else { echo "connect success\n"; } }); }; $worker->onMessage = function($connection, $data) { global $mysql; $mysql->query('show databases', function ($command, $mysql) use ($connection) { if ($command->hasError()) { $error = $command->getError(); } else { $results = $command->resultRows; $fields = $command->resultFields; $connection->send(json_encode($results)); } }); }; Worker::runAll();2. 异步 Redis
composer require clue/redis-react<?php require_once __DIR__ . '/vendor/autoload.php'; use Clue\React\Redis\Factory; use Clue\React\Redis\Client; use Workerman\Worker; $worker = new Worker('tcp://0.0.0.0:6161'); $worker->onWorkerStart = function() { global $factory; $loop = Worker::getEventLoop(); $factory = new Factory($loop); }; $worker->onMessage = function($connection, $data) { global $factory; $factory->createClient('localhost:6379')->then(function (Client $client) use ($connection) { $client->set('greeting', 'Hello world'); $client->append('greeting', '!'); $client->get('greeting')->then(function ($greeting) use ($connection){ echo $greeting . PHP_EOL; $connection->send($greeting); }); $client->incr('invocation')->then(function ($n) use ($connection){ echo 'This is invocation #' . $n . PHP_EOL; $connection->send($n); }); }); }; Worker::runAll();3. 异步 DNS 解析
composer require react/dnsrequire_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; $worker = new Worker('tcp://0.0.0.0:6161'); $worker->onWorkerStart = function() { global $dns; $loop = Worker::getEventLoop(); $factory = new React\Dns\Resolver\Factory(); $dns = $factory->create('8.8.8.8', $loop); }; $worker->onMessage = function($connection, $host) { global $dns; $host = trim($host); $dns->resolve($host)->then(function($ip) use($host, $connection) { $connection->send("$host: $ip"); },function($e) use($host, $connection){ $connection->send("$host: {$e->getMessage()}"); }); }; Worker::runAll();4. 异步 HTTP 客户端
composer require react/http-client<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; $worker = new Worker('tcp://0.0.0.0:6161'); $worker->onMessage = function($connection, $host) { $loop = Worker::getEventLoop(); $client = new \React\HttpClient\Client($loop); $request = $client->request('GET', trim($host)); $request->on('error', function(Exception $e) use ($connection) { $connection->send($e); }); $request->on('response', function ($response) use ($connection) { $response->on('data', function ($data) use ($connection) { $connection->send($data); }); }); $request->end(); }; Worker::runAll();5. 异步 ZMQ(消息队列)
composer require react/zmq<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; $worker = new Worker('text://0.0.0.0:6161'); $worker->onWorkerStart = function() { global $pull; $loop = Worker::getEventLoop(); $context = new React\ZMQ\Context($loop); $pull = $context->getSocket(ZMQ::SOCKET_PULL); $pull->bind('tcp://127.0.0.1:5555'); $pull->on('error', function ($e) { var_dump($e->getMessage()); }); $pull->on('message', function ($msg) { echo "Received: $msg\n"; }); }; Worker::runAll();6. 异步 STOMP(消息代理协议)
composer require react/stomp<?php require_once __DIR__ . '/vendor/autoload.php'; use Workerman\Worker; $worker = new Worker('text://0.0.0.0:6161'); $worker->onWorkerStart = function() { global $client; $loop = Worker::getEventLoop(); $factory = new React\Stomp\Factory($loop); $client = $factory->createClient(array('vhost' => '/', 'login' => 'guest', 'passcode' => 'guest')); $client ->connect() ->then(function ($client) use ($loop) { $client->subscribe('/topic/foo', function ($frame) { echo "Message received: {$frame->body}\n"; }); }); }; Worker::runAll();八、命令行管理:启动、状态、停止与重载
Workerman 官方 README 给出了一套进程管理命令(在start.php场景下):
php start.php start # 前台启动 php start.php start -d # 守护进程方式启动 php start.php status # 查看运行状态 php start.php connections # 查看当前连接数 php start.php stop # 停止 php start.php restart # 重启 php start.php reload # 平滑重载(仅 reloadable 的进程)命令的第一个参数start/-d/status/connections/stop/restart/reload会被映射为内部信号操作。从 Worker.php 的状态常量可见进程存在STATUS_STARTING / STATUS_RUNNING / STATUS_SHUTDOWN / STATUS_RELOADING四种状态。
CRMEB 中的命令封装
CRMEB 通过 ThinkPHP 6 控制台命令封装了上述能力(见 crmeb/README.md):
# 长连接服务(h5 聊天、后台管理员消息通知、新订单弹窗) php think workerman [status] [--d]参数说明:
status:start(启动)/stop(关闭)/restart(重启)/status/connections;--d:守护进程(后台)方式启动;- 第二个参数
server(可选):admin/chat/channel,可只启动其中某一个服务。
命令实现位于 crmeb/crmeb/command/Workerman.php:
init()把 ThinkPHP 的Input参数回填到全局$argv,从而复用 Workerman 的信号解析逻辑;execute()读取 config/workerman.php 配置,依次创建admin Worker(websocket://0.0.0.0:40001)、chat Worker(websocket://0.0.0.0:40002)、channel 内部通讯服务(127.0.0.1:40003);- 各服务进程数由配置中的
serverCount决定(当前均为 1); - PID 文件写入
runtime/workerman.pid; - 最后调用
Worker::runAll()统一启动。
命令注册在 crmeb/config/console.php:workerman→\crmeb\command\Workerman::class,timer→\crmeb\command\Timer::class。
九、CRMEB 中的落地实现:配置、鉴权与消息分发
1. 配置项速查
config/workerman.php 是三段式结构,与上述三个服务一一对应:
| 配置段 | 协议 | 监听地址 | 端口 | 进程数 | 用途 |
|---|---|---|---|---|---|
channel | 内部 TCP | 127.0.0.1 | 40003 | — | 程序与长连接服务之间的内部通讯总线 |
admin | websocket | 0.0.0.0 | 40001 | 1 | 后台新订单/退款通知、管理员消息推送 |
chat | websocket | 0.0.0.0 | 40002 | 1 | 客户与客服的实时聊天 |
2. 鉴权流程:Token → 连接映射
以 admin 服务为例,WorkermanHandle.php 的login方法处理前端 WebSocket 传来的type=login消息:从data中取出管理员 Token,调用AdminAuthServices::parseToken()校验,成功后将adminInfo/adminId挂在TcpConnection对象上,并通过WorkermanService::setUser()建立「管理员 ID → 连接」映射;鉴权失败则直接close连接。chat 服务(ChatService.php)同样维护$user(普通用户)与$kefuUser(在线客服)两张映射表。
3. Channel 消息总线与广播
多进程/多服务之间通过 workerman-channel 组件通信:
- ChannelService.php 封装了
Client::connect($config['ip'], $config['port'])、send($type, $data, $ids)与Client::publish($type, $data);send()中的ids为可选的指定用户 ID 列表,不传则广播给全部在线用户; - 业务侧(订单创建、退款等)向 channel 发布
crmeb/crmeb_chat主题消息; - WorkermanService.php 在
onWorkerStart中Client::on('crmeb', ...)订阅,收到后按ids遍历$user表逐连接下发; - ChatService.php 同样订阅
crmeb_chat,若事件携带fun标记则调用ChatHandle对应方法处理,否则直接推送type + data。
4. 统一响应封装
Response.php 提供链式发送工具:connection($conn)->success($type, $data)发送成功消息、fail()发送失败消息、close()发送后关闭连接,最终统一json_encode后send,保证前后端消息格式一致(形如{"type":"...","data":{...}})。
5. 心跳与掉线回收
onMessage每次都会刷新$connection->lastMessageTime(WorkermanService.php);前端心跳类型为ping时服务端直接回ping + now时间戳。配合前述 Timer 扫描,超过 12 秒无消息即判定超时关闭,避免僵尸连接堆积。
十、性能基准与源码目录速览
官方基准测试(硬件环境与结论)
Workerman 官方 README 附带了在如下环境的 ab 压测记录(结果仅供参考,实际表现取决于服务器硬件与业务复杂度):
- CPU:Intel(R) Core(TM) i3-3220 @ 3.30GHz(4 处理器)
- 内存:8G
- 操作系统:Ubuntu 14.04 LTS
- 压测工具:ApacheBench(ab)
- PHP:5.5.9
压测代码是一个 3 进程的纯 TCP 服务,onMessage直接回写一个 5 字节 HTTP 响应:
<?php use Workerman\Worker; $worker = new Worker('tcp://0.0.0.0:1234'); $worker->count=3; $worker->onMessage = function($connection, $data) { $connection->send("HTTP/1.1 200 OK\r\nConnection: keep-alive\r\nServer: workerman\r\nContent-Length: 5\r\n\r\nhello"); }; Worker::runAll();压测命令与核心结果:
ab -n1000000 -c100 -k http://127.0.0.1:1234/- 完成请求:1,000,000;失败请求:0;Keep-Alive 请求:1,000,000;
- Requests per second:138124.14 [#/sec](平均);
- 平均单请求耗时 0.724ms,99% 请求在 1ms 内完成;
- Transfer rate:9846.74 KB/s。
需要强调的是:该数据是 Workerman 3.1.4 在特定单机环境下的官方记录,属 README 自带历史数据,不应作为当前仓库或生产环境的通用结论。
仓库内目录结构速览
Workerman 在crmeb/vendor/workerman/workerman下的模块分布:
Worker.php:进程容器、信号处理、runAll()入口(版本 3.5.19);WebServer.php:HTTP 静态服务器(虚拟主机 + MIME 映射);Connection/:TcpConnection、UdpConnection、AsyncTcpConnection、AsyncUdpConnection;Events/:Select、Event、Libevent、Swoole及 React 系列事件循环;Lib/:Timer、Constants;Protocols/:Http、Websocket、Ws、Text、Frame、ProtocolInterface及Http/mime.types;Autoloader.php:内置自动加载器。
CRMEB 侧对 Workerman 的二次封装位于 crmeb/crmeb/services/workerman:WorkermanService(admin 推送)、chat/ChatService(客服聊天)、ChannelService(消息总线)、Response(统一响应)、WorkermanHandle/chat/ChatHandle(消息路由与业务处理)。
十一、许可说明
Workerman 以 MIT 许可证开源(见 MIT-LICENSE.txt),可自由用于商业项目。本文所述示例均可在仓库内对应的源码与配置文件中直接查阅验证,建议结合 config/workerman.php、crmeb/crmeb/command/Workerman.php 与 crmeb/README.md 三个文件对照阅读,即可快速搭建出属于自己的长连接业务。
- 后端
- 电商
- 前端
【免费下载链接】CRMEB
🔥CRMEB开源商城免费开源多语言商城系统,Tp6框架商城,系统可商用;包含小程序商城、H5商城、公众号商城、PC商城、App,支持分销、拼团、砍价、秒杀、优惠券、积分、会员等级、小程序直播、页面DIY,前后端分离,方便二开,更有详细使用文档、接口文档、数据字典、二开文档/视频教程,欢迎大家提出宝贵意见和建议
相关推荐
GoBelieve即时通讯服务教程
GoBelieve即时通讯服务教程 项目介绍 GoBelieve是一个开源的即时通讯服务项目,旨在为开发者提供一个高效、稳定、易用的即时通讯解决方案。该项目支持
终极指南:如何使用CocoaAsyncSocket实现iOS即时通讯长连接
终极指南:如何使用CocoaAsyncSocket实现iOS即时通讯长连接 CocoaAsyncSocket是一款强大的异步网络通信库,专为Mac和iOS平台设
通信考研408数据结构怎么拿高分:一份四阶段可执行的完整复习指南
考研408数据结构怎么拿高分:一份四阶段可执行的完整复习指南 备考考研408数据结构时,你大概率经历过这样的场景:视频里快速排序看得明明白白,合上书却要手写出来
文档教育教程
创作声明:本文部分内容由AI辅助生成(AIGC),仅供参考