[TOC] ## 说明: 插件是基于`workerman`实现的websocket即时通讯。 `workerman`文档地址:https://www.workerman.net/doc/workerman/ 最早本来是想基于[https://www.workerman.net/phpsocket_io](https://www.workerman.net/phpsocket_io)实现通讯,但是由于PHP版本问题(我们需要高版本环境下开发)有较大兼容问题,而该扩展本身又不再更新。因此自己做了一个简单以事件为驱动的websocket通讯用于满足开主业务开发过程中需要websocket即时通讯辅助开发。 所以你的主业务只要不是即时通讯类工具,可以考虑使用该插件;否则,建议使用其他成熟方案。 该插件只提供通讯服务,具体业务需自己整合和开发。 WOOAdmin官方严正声明: 为配合国家打击跨境赌博、电信诈骗、断卡行动,本站所有源码仅限中国大陆区域使用且使用站点要通过ICP备案,否则一律上报至公安机关! ## 安装和启动: 1、安装`workerman` ~~~ composer require workerman/workerman ~~~ 2、下载并解压到addons目录下 3、后台开发管理->插件管理中,找到“websocket”插件点击安装 4、新增命令:在`config/console.php`新增一个ws的命令 ~~~ 'ws' => 'addons\websocket\service\command\Ws' ~~~ 5、命令: ~~~ php think ws start -m // 启动服务 php think ws stop -m // 停止服务 php think ws restart -m // 重启服务 ~~~ ## 配置: `config/websocket.php`下: ~~~ <?php return [ 'worker_name' => 'WebsocketWorker',//设置当前Worker实例的名称,方便运行status命令时识别进程 'daemonize' => true,// 此属性为全局静态属性,表示是否以daemon(守护进程)方式运行 'log_file' => runtime_path() . 'websocket.log', 'pid_file' => runtime_path() . 'websocket.pid', 'count' => 1, //设置当前Worker实例启动多少个进程 https://www.workerman.net/doc/workerman/worker/count.html 'server' => env('WS_SERVER', 'websocket://0.0.0.0:8282'), 'client' => env('WS_CONNECT_HOST', 'ws://127.0.0.1:8282'), 'listen_strict' => true,// true 就是所有事件都必须注册 'check_origin' => false, // 注册工作类 'worker' => [ 'demo' => [ 'handler' => app\common\worker\Demo::class, 'listen' => [ // listen_strict true:就所有事件都要注册,否则可以不注册 'initMessage' => 'initMessage', 'mySend' => 'mySend'// 自定义事件 ] ] ] ]; ~~~ `server`:启动端口,如果本地和服务器不一样可以在.end中`WS_SERVER`定义 `client`:客户端链接地址 `worker`:所有工作类和事件都要在这里面的进行注册 ## 后端服务工作类 ~~~ <?php declare (strict_types = 1); namespace app\common\worker; use addons\websocket\service\WebSocketManager; use Workerman\Connection\TcpConnection; class Demo extends WebSocketManager { public function onConnect(TcpConnection $connection) { $params = $connection->requestParams ?? []; $this->send($connection, 'myConnect', 'success', '链接成功', ['id' => $connection->id, 'params' => $params]); } public function mySend(TcpConnection $connection, $event, $data) { $this->send($data['clientId'], 'myReceive', 'success', '成功', ['fromId' => $connection->id, 'message' => $data['message']]); } public function myJoin(TcpConnection $connection, $event, $data) { $this->join($data['room'], $connection); } public function myRoomSeed(TcpConnection $connection, $event, $data) { $this->sendToRoom($data['room'], 'myRoomReceive', 'success', '成功', ['from' => $data['room'], 'message' => $data['message']]); } } ~~~ 需要继承`\addons\websocket\service\WebSocketManager`类。 后端代码有改动,需重启服务以后生效。 可调用方法: ~~~ /** * 加入房间 * @param string|int|float $room * @param TcpConnection|int $connection * @return void */ public function join(string|int|float $room, TcpConnection|int $connection) ~~~ ~~~ /** * 离开房间 * @param string|int|float $room * @param TcpConnection|int $connection * @return void */ public function leave(string|int|float $room, TcpConnection|int $connection) ~~~ ~~~ /** * 给指定客户端发送消息 * @param $connection 可以是具体连接对象,也可以是某个连接的id * @param $event 事件名 * @param $status 状态 * @param $message 消息 * @param $data 数据 * @return true */ public function send(TcpConnection|int $connection, string $event, $status = 'success', string $message = '', $data = null) ~~~ ~~~ /** * 向所有客户端发送消息 * @param string $event * @param $status 状态 * @param string $message * @param $data * @return true */ public function sendAll(string $event, $status = 'success', string $message = '', $data = null) ~~~ ~~~ /** * 向所有客户端发送事件,但不包括指定客户端 * @param TcpConnection|int $connection * @param string $event * @param $status 状态 * @param string $message * @param $data * @return true */ public function sendExceptSelf(TcpConnection|int $connection, string $event, $status = 'success', string $message = '', $data = null) ~~~ ~~~ /** * 向某个分组的所有客户端发送事件 * @param string|int|float $room * @param string $event * @param $status 状态 * @param string $message * @param $data * @return true */ public function sendToRoom(string|int|float $room, string $event, $status = 'success', string $message = '', $data = null) ~~~ ~~~ /** * 连接的时候 * @param $connection * @return void */ public function onConnect(TcpConnection $connection) ~~~ ~~~ /** * 有消息的时候 * @param $connection * @param $event * @param $message * @return void */ public function onMessage(TcpConnection $connection, $event, $message) ~~~ ~~~ /** * 有连接关闭的时候 * @param $connection * @return void */ public function onClose(TcpConnection $connection) ~~~ ## 客户端 客户端请自己发挥。 这里给了要给简单的demo:http://www.域名.com/addons/websocket/index/index.html。 系统默认封装了一个客户端类:`public/static/addons/websocket/js/socket.js` 以下是使用该客户端的demo: ~~~ var ws = new WebSocketClient({ worker : 'demo',// Worker名 url : '{$websocket_client}?login=2&id=5&a=aa&b=bb', callback: { // 错误事件 error: function (status, msg, data) { console.log(status, msg) }, // 统一事件 onopen: function () { //console.log('onopen'); }, onclose: function () { //console.log('onclose'); }, onmessage: function (message) { //console.log(message) }, // 自定义事件 参数:状态 消息 数据 myConnect: function (status, message, data) { console.log(status, message, data) }, myReceive: function (status, message, data) { console.log(status, message, data) $('#messageBox').append('<p>来自'+ data.fromId + '发送的消息:'+ data.message +'</p>') }, myRoomReceive: function (status, message, data) { console.log(status, message, data) $('#messageBox').append('<p>群里消息:'+ data.message +'</p>') }, } }) ws.createWebSocket();// 链接 $('#sendMsg').click(function () { ws.send('mySend', { clientId : $('#client_id').val(), message : $('#message').val(), }) }) $('#joinRoom').click(function () { ws.send('myJoin', { room : 'aaa' }) }) $('#joinMsg').click(function () { ws.send('myRoomSeed', { room : 'aaa', message : $('#message').val(), }) }) ~~~ ws.createWebSocket() :打开要给链接 ws.send('事件名', 消息数据):发送消息 ## wss ~~~ var ws = new WebSocketClient({ worker : 'demo',// Worker名 url : wss://域名:100/wss?login=2&id=5&a=aa&b=bb', ... ~~~ nginx配置: ~~~ location /wss { proxy_pass http://127.0.0.1:8282; proxy_http_version 1.1; proxy_set_header Upgrade $http_upgrade; proxy_set_header Connection "Upgrade"; proxy_set_header X-Real-IP $remote_addr; } ~~~