http协议推送websocket
在 think-swoole 中,如果需要通过 HTTP 接口(控制器)向指定的 WebSocket 客户端推送消息,你不能直接在控制器里使用 $websocket 变量,因为 HTTP 和 WebSocket 是两个独立的请求上下文。
要实现跨协议推送,必须使用 think-swoole 提供的 Swoole 场景服务(Context)。以下是具体的实现方案:
1. 控制器中实现 HTTP 推送
在你的控制器(Controller)方法中,注入 think\swoole\Websocket 类,并使用 to() 方法指定要接收消息的 fd(客户端标识),最后通过 push() 或 emit() 发送。
方案 A:原生 WebSocket 驱动
如果你使用的是标准原生 WebSocket,请使用 push() 方法:
php
<?php
namespace app\controller;
use think\Request;
use think\swoole\Websocket;
class PushController
{
public function sendToUser(Request $request, Websocket $websocket)
{
// 从 HTTP 请求中获取接收者的 fd 和要发送的内容
$fd = $request->param('fd');
$msg = $request->param('message', '系统测试消息');
if (!$fd) {
return json(['code' => 400, 'msg' => '缺少参数 fd']);
}
// 指定 fd 并直接推送消息
$websocket->to(intval($fd))->push($msg);
return json(['code' => 200, 'msg' => "成功推送到客户端 FD: {$fd}"]);
}
}
请谨慎使用此类代码。
方案 B:Socket.io 驱动
如果你在 config/swoole.php 中配置的是 socketio 驱动,请使用 emit() 方法:
php
// 指定 fd,触发前端监听的自定义事件(例如 'notice')
$websocket->to(intval($fd))->emit('notice', [
'title' => '系统通知',
'content' => $msg
]);
请谨慎使用此类代码。
2. 常见业务场景:如何通过“用户 ID”推送?
在实际开发中,HTTP 接口通常只知道 user_id(用户ID),而不知道 Swoole 随机分配的 fd。因此,你需要在 WebSocket 连接成功(open 或 connect 事件)时,将两者绑定起来并存入 Redis。
步骤一:连接时绑定并存入 Redis
php
// app/listener/WsOpen.php (以原生 Websocket 为例)
namespace app\listener;
use think\swoole\Websocket;
use think\facade\Cache;
class WsOpen
{
public function handle($event, Websocket $websocket)
{
$fd = $websocket->getSender();
// 假设从连接 URL 参数中拿到了当前登录的 userId
$userId = request()->param('user_id');
if ($userId) {
// 将 userId 和 fd 的对应关系存入 Redis(有效期根据业务定)
Cache::store('redis')->set("uid_to_fd:{$userId}", $fd, 7200);
}
}
}
请谨慎使用此类代码。
步骤二:HTTP 接口通过用户 ID 查找并推送
php
// app/controller/PushController.php
public function sendToUid(Request $request, Websocket $websocket)
{
$userId = $request->param('user_id');
$msg = $request->param('message');
// 从 Redis 中取出该用户对应的 fd
$fd = \think\facade\Cache::store('redis')->get("uid_to_fd:{$userId}");
if (!$fd) {
return json(['code' => 404, 'msg' => '用户不在线']);
}
// 推送
$websocket->to(intval($fd))->push($msg);
return json(['code' => 200, 'msg' => '推送成功']);
}
请谨慎使用此类代码。
3. 全局广播(推送给所有人)
如果你希望通过 HTTP 接口向所有当前连接的客户端群发消息,可以结合 broadcast() 方法使用:
php
// 广播给所有人 (原生 WebSocket)
$websocket->broadcast()->push("这是系统全局广播消息");
// 广播给所有人 (Socket.io 驱动)
$websocket->broadcast()->emit('allNotice', ['msg' => '全服通知']);
请谨慎使用此类代码。
接下来,你可以尝试创建一个 HTTP 路由来测试这个推送功能。