ThinkPHP5 利用 WebSocket 实现全站公告推送
WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。
最近在开发 MoeCTF
中的 公告实时推送
功能,本来想通过 轮询
实现,但是想了一下 轮询
是客户端主动向服务端发送请求,如果单个用户感觉还行,但如果在线的用户过多均通过 轮询
的方式进行请求,容易造成大量无用请求,从而导致负载过高。
所以打算使用 WebSocket
来进行一次长连接,减少请求。由于对 WebSocket
没有过多的要求,所以直接选择了基于 WorkerMan
开发的 GatewayWorker
安装
ThinkPHP5
只能安装 2.*
版本
1
| composer require topthink/think-worker=2.0.*
|
Events
修改 vendor/topthink/think-worker/src/Events.php
内的 onConnect
和 onMessage
方法
1 2 3 4 5 6 7 8 9 10 11 12 13 14
| public static function onConnect($client_id) { Gateway::sendToClient($client_id, json_encode(array( 'type' => 'init', 'client_id' => $client_id ))); }
public static function onMessage($client_id, $message) {
}
|
运行
注意每一次修改 WorkerMan
的配置后都需要重新启动
1
| php think worker:gateway
|
控制器
当服务端收到连接请求时,判断用户是否登录,如果已经登录把 client_id
添加到群组 All
中
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32
| <?php namespace app\index\controller; use GatewayWorker\Lib\Gateway; use think\facade\Session; use think\Controller;
class Push extends controller { public function initialize() { parent::initialize(); if (!Session::has('uid') { returnJsonData(201, 'Please login first')->send(); exit; } } public function index($data){ $data = [ 'type' => 'notify', 'msg' => input('msg'), ]; Gateway::sendToGroup('All', json_encode($data)); }
public function notify() { Gateway::$registerAddress = '127.0.0.1:1236'; $client_id = $_POST['client_id']; Gateway::joinGroup($client_id, 'All'); } }
|
用户界面
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20
| ws = new WebSocket("ws://127.0.0.1:2348"); ws.onmessage = function (e) { var data = eval("(" + e.data + ")"); var type = data.type || ""; switch (type) { case "init": $.post( "/api/v1/notify", { client_id: data.client_id }, function (data) {}, "json" ); break; case "notify": console.log('notify:' + data); break; default: console.log(data); } };
|
效果
管理员通过 push
方法请求的数据会推送给所有登录用户。