ThinkPHP5 利用 WebSocket 实现全站公告推送
3 min read
WebSocket是一种在单个TCP连接上进行全双工通信的协议,允许服务端主动向客户端推送数据。
最近在开发 MoeCTF
中的 公告实时推送
功能,本来想通过 轮询
实现,但是想了一下 轮询
是客户端主动向服务端发送请求,如果单个用户感觉还行,但如果在线的用户过多均通过 轮询
的方式进行请求,容易造成大量无用请求,从而导致负载过高。
所以打算使用 WebSocket
来进行一次长连接,减少请求。由于对 WebSocket
没有过多的要求,所以直接选择了基于 WorkerMan
开发的 GatewayWorker
安装
ThinkPHP5
只能安装 2.*
版本
composer require topthink/think-worker=2.0.*
Events
修改 vendor/topthink/think-worker/src/Events.php
内的 onConnect
和 onMessage
方法
// 当有客户端连接时,将client_id返回,让mvc框架判断当前uid并执行绑定
public static function onConnect($client_id)
{
Gateway::sendToClient($client_id, json_encode(array(
'type' => 'init',
'client_id' => $client_id
)));
}
// GatewayWorker建议不做任何业务逻辑,onMessage留空即可
public static function onMessage($client_id, $message)
{
}
运行
注意每一次修改 WorkerMan
的配置后都需要重新启动
php think worker:gateway
控制器
当服务端收到连接请求时,判断用户是否登录,如果已经登录把 client_id
添加到群组 All
中
<?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');
}
}
用户界面
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
方法请求的数据会推送给所有登录用户。