avatar

mdo

Hello

  • 首页
  • 知识库
  • 归档
  • 标签
  • 关于
主页 think-swoole-websocket
文章

think-swoole-websocket

发表于 2026-06-16 更新于 2026-06- 16
作者 mdo
14~18 分钟 阅读

在 ThinkPHP 6 (TP6) 中使用 think-swoole (通常对应 v3.x 版本插件) 实现 WebSocket,核心逻辑是基于事件(Event)驱动。

以下是针对 TP6 开发环境的实战配置、多房群聊(房间)、在线状态追踪以及 Nginx 反向代理的完整落地方案。


1. 核心文件结构

在 TP6 中,建议将 WebSocket 相关的事件监听类统一归类。结构如下:

text

app/
├── listener/
│   ├── WsConnect.php    # 监听连接
│   ├── WsClose.php      # 监听断开
│   └── WsMessage.php    # 监听自定义业务消息
config/
└── swoole.php           # swoole 配置文件

请谨慎使用此类代码。


2. 精准事件配置 (config/swoole.php)

TP6 中,think-swoole 内置了类似 socket.io 的分发机制。你需要将底层事件映射到你的全局监听类中:

php

return [
    'server' => [
        'host' => '0.0.0.0',
        'port' => 9501, // 建议避开常规端口
    ],
    'websocket' => [
        'enable' => true,
        'handler' => \think\swoole\websocket\Handler::class,
        'listen' => [
            // 基础连接事件
            'connect' => \app\listener\WsConnect::class,
            'close'   => \app\listener\WsClose::class,
            
            // 自定义业务事件(客户端发送不同的事件名,路由到对应的类)
            'join'    => \app\listener\WsMessage::class, // 加入房间
            'chat'    => \app\listener\WsMessage::class, // 聊天
        ],
    ],
    // 必须开启 RPC 或常驻内存相关的沙箱,确保多线程下容器正常工作
    'sandbox' => [
        'child_status' => true,
    ],
];

请谨慎使用此类代码。


3. 事件处理器编写 (基础与业务)

think-swoole 在事件触发时会依赖依赖注入。你可以直接在 handle 方法中注入 think\swoole\Websocket 对象和当前的数据。

A. 连接管理与用户绑定 (app/listener/WsConnect.php)

php

namespace app\listener;

use think\swoole\Websocket;

class WsConnect
{
    public function handle(Websocket $ws)
    {
        // 获取当前连接的客户端文件描述符 (fd)
        $fd = $ws->getSender();
        
        // 建议:此处可以通过握手参数(如:ws://url?token=xxx)获取用户信息
        // 并将 uid 与 fd 的映射关系存入 Redis,用于之后的精准推送
        
        $ws->emit('connected', ['fd' => $fd, 'msg' => '连接成功']);
    }
}

请谨慎使用此类代码。

B. 核心业务:房间与聊天 (app/listener/WsMessage.php)

think-swoole 提供了非常方便的 join, leave, to(房间隔离)方法:

php

namespace app\listener;

use think\swoole\Websocket;

class WsMessage
{
    protected $ws;

    // 构造函数注入 Websocket 实例
    public function __construct(Websocket $ws)
    {
        $this->ws = $ws;
    }

    public function handle($data)
    {
        // $data 是客户端发送过来的载荷数据
        // 配合 Swoole 的沙箱,这里可以直接调用系统方法
        
        // 伪代码:通过当前事件名称执行不同逻辑
        // 注:TP6 事件分发时,如果多个事件指向同一个类,可以通过辅助手段或拆分不同类处理。
        // 推荐:为不同的业务事件建立独立的 Listener 类(如 WsChat, WsJoin)
    }
    
    /**
     * 场景一:加入房间逻辑(可放于单独的 WsJoin 监听器中)
     */
    public function joinRoom($data)
    {
        $roomName = $data['room'] ?? 'default_room';
        $currentFd = $this->ws->getSender();
        
        // 将当前连接加入指定房间
        $this->ws->join($roomName);
        
        // 通知当前房间内的所有人(不含自己用 to($room)->broadcast(),含自己用 to($room))
        $this->ws->to($roomName)->emit('sys_msg', "用户 {$currentFd} 加入了房间");
    }

    /**
     * 场景二:群发聊天逻辑(可放于单独的 WsChat 监听器中)
     */
    public function sendChat($data)
    {
        $roomName = $data['room'] ?? 'default_room';
        $message  = $data['content'] ?? '';

        // 推送给房间内的所有人
        $this->ws->to($roomName)->emit('new_chat', [
            'from'    => $this->ws->getSender(),
            'content' => $message,
            'time'    => date('H:i:s')
        ]);
    }
}

请谨慎使用此类代码。


4. 前端 JS 调用规范

think-swoole 底层对原生 WebSocket 数据包做了封装。前端发送的数据格式必须是 JSON 数组,格式为 ["事件名", 数据对象]:

javascript

const ws = new WebSocket('ws://127.0.0.1:9501');

ws.onopen = () => {
    console.log('WS 连接成功');
    
    // 1. 发送加入房间事件
    ws.send(JSON.stringify([
        'join', 
        { room: 'room_101' }
    ]));
};

ws.onmessage = (res) => {
    // 服务端返回的数据也是标准 JSON
    const [event, data] = JSON.parse(res.data);
    
    if (event === 'sys_msg') {
        console.log('系统通知:', data);
    }
    if (event === 'new_chat') {
        console.log('收到消息:', data.from + ': ' + data.content);
    }
};

请谨慎使用此类代码。


5. 生产环境配置:Nginx 反向代理 (支持 HTTPS/WSS)

在生产环境中,严禁直接暴露 9501 端口。应使用 Nginx 作为前置代理,转发 WebSocket 连接并配置 SSL:

nginx

server {
    listen 80;
    listen 443 ssl;
    server_name yourdomain.com;

    ssl_certificate /path/to/ssl.crt;
    ssl_certificate_key /path/to/ssl.key;

    # 正常的 HTTP 业务走 TP6 
    location / {
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        if (!-e $request_filename) {
             proxy_pass http://127.0.0.1:9501;
        }
    }

    # 特定的 /ws 路径走 WebSocket 转发
    location /ws {
        proxy_pass http://127.0.0.1:9501; # 指向 TP6 Swoole 端口
        proxy_http_version 1.1;
        
        # 核心:升级协议为 WebSocket
        proxy_set_header Upgrade $http_upgrade;
        proxy_set_header Connection "Upgrade";
        
        proxy_set_header Host $http_host;
        proxy_set_header X-Real-IP $remote_addr;
        proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
        
        # 延长超时时间,防止 Nginx 自动断开长连接(根据业务调整,单位:秒)
        proxy_read_timeout 600s;
        proxy_send_timeout 600s;
    }
}

请谨慎使用此类代码。

前端连接地址顺应改为:wss://://yourdomain.com


如果想继续深入开发,可以告诉我:

  • 需要引入 Redis 来持久化存储用户在线状态(Fd 与 UserID 绑定)的代码吗?

  • 业务中是否需要实现**点对点私聊(指定 uid 发送)**而不仅仅是房间群聊?

  • 是否遇到了代码修改后,Swoole 热重载(Hot Reload)失效的问题?

技术
许可协议:  CC BY 4.0
分享

相关文章

7月 26, 2026

完美地解决 TP3 老系统数据的平滑读取

为了彻底、完美地解决 TP3 老系统数据的平滑读取,必须解决两个核心痛点: TP3 的盲猜反序列化机制(标量存原样,数组/对象存原生 serialize,同时兼容 json)。 TP3 与 TP6 的缓存前缀(Prefix)不一致。 以下是为你量身定制的终极落地解决方案,通

7月 26, 2026

thinkphp3 redis序列化和反序列化

在老系统重构迁移至 ThinkPHP 6 (TP6) 的过程中,ThinkPHP 3 (TP3) 的 Redis 序列化历史遗留问题是最核心的连环坑之一。 TP3 的 S('key', $value) 缓存方法和底层 Redis 驱动,在存储非字符串(如数组、对象)时,有一套独特的序列化行为。如果不

7月 24, 2026

Table 空间极易发生哈希冲突并溢出

既然你没有在业务中主动调用 Room,只使用了全局广播,那么这个问题就非常明确了:即使你不用房间功能,think-swoole 只要开启了 WebSocket,底层就会强行初始化并注入一个名为 Room 的驱动组件。 [1] 框架底层默认使用 table 类型来作为 Room 的存储介质。当进行全局

下一篇

think-swoole WebSocket 的核心步骤与代码实现

上一篇

Swoole http server 如何处理http消息推送

最近更新

  • 完美地解决 TP3 老系统数据的平滑读取
  • thinkphp3 redis序列化和反序列化
  • Table 空间极易发生哈希冲突并溢出
  • 将监控程序直接跑在云端
  • AI 驱动型 Facebook 群组关键词监控 Chrome 浏览器插件

热门标签

API CodeGeex Gitkraken Management Manticore Premiere Sublime Swoole ThinkPHP ThinkPHP5

目录

©2026 mdo. 保留部分权利。