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
分享

相关文章

10月 2, 2026

iOS 侧载(Sideloading)工具

AltStore 和 Sideloadly 都是目前最流行的 iOS 侧载(Sideloading)工具。 简单来说,它们的作用就是让你绕过苹果的官方 App Store,直接把第三方的 IPA 安装包(也就是 iOS 的应用安装文件)安装到你的 iPhone 或 iPad 上。 通过这两个工具,你

9月 28, 2026

注册 Chat Participant

将当前服务接入 VS Code 的 AI 聊天窗口是完全可行的。VS Code 在近期的版本中正式推出了 Chat Extensions API(github.copilot.chat),允许第三方扩展将自定义的 LLM 服务或工具注册到 Copilot Chat 面板中。 结合当前 code_ge

9月 23, 2026

php intelephense 设置 if 空格

在 VS Code 中,如果你使用的是 PHP Intelephense 插件,控制 if 关键字后面是否加空格的设置是由其内置的格式化引擎(基于 PSR-12 标准)决定的。 你可以通过修改 VS Code 的 settings.json 来配置这个行为: 1. 开启 if 后面的空格(默认/推荐

下一篇

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

上一篇

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

最近更新

  • 比尔盖茨的智慧:想多赚钱,就每天循环做这3件事
  • iOS 侧载(Sideloading)工具
  • 程序员越想创业,越不要急着动手
  • 注册 Chat Participant
  • 中秋给在外游子的一封信

热门标签

API CodeGeex Coding Cursor DeepSeek Docker Gitkraken Harness Laravel Management

目录

©2026 mdo. 保留部分权利。