codeigniter 3.x 无法直接处理 websocket,因 php-fpm 生命周期结束即销毁进程,ratchet 必须作为独立常驻服务运行,与 ci 共享数据不共享实例,需手动管理连接、配置和资源。

CodeIgniter 3.x 本身不能直接处理 WebSocket 连接——onOpen、onMessage 等回调写在控制器里根本不会执行,因为 PHP-FPM 请求生命周期一结束,进程就销毁,ConnectionInterface 实例随之丢失。
为什么不能把 WebSocket 逻辑塞进 CI 控制器
WebSocket 握手虽以 HTTP GET 请求发起(带 Upgrade: websocket 头),但后续通信完全脱离 HTTP 生命周期。Apache/Nginx 调用 Chat.php 后,响应返回即终止进程;你看到的 “New connection!” 日志只是握手瞬间的残留输出,几秒后连接必然断开,onMessage 永远不会触发。
常见错误包括:
- 在控制器中
new Ratchet\App或调用$this->ci->load->model()——get_instance()返回null,报Fatal error: Call to a member function load() on null - 试图复用 CI 的
$this->session或自动加载的库 ——application/config/autoload.php在 Ratchet 独立进程中完全不生效 - 把
chat_server.php放进application/controllers/目录下并期望它被路由访问 —— 它不是控制器,也不能被 Web 服务器直接执行
必须用 Ratchet 或 Workerman 启动独立常驻服务
Ratchet 是最常用的 PHP WebSocket 库,需通过 Composer 安装,并作为后台守护进程运行,与 CI 应用并行存在。二者之间只共享数据,不共享内存或对象实例。
实操要点:
- 在项目根目录执行:
composer require cboden/ratchet:^0.4.4(注意 PHP 版本 ≥ 7.2,且启用sockets扩展) - 创建独立启动脚本(如
chat_server.php),不在application/下,也不经由index.php入口 - 脚本中绕过 CI 引导:用原生
PDO或new Redis()连数据库/缓存,不要调用get_instance() - 用户登录成功后,CI 写一条含
user_id和临时token的记录到ws_sessions表;Ratchet 在onOpen时解析 URL 参数(如ws://localhost:8080?token=abc123)查表绑定身份
消息广播必须查表获取在线连接 ID 列表
CI 控制器发消息时,不能直接调用 WebSocket 服务的方法。正确路径是:CI 将消息存入数据库 → Ratchet 进程定时轮询或监听数据库变更(如 MySQL binlog)→ 查 ws_sessions 表拿到当前有效的 resourceId 列表 → 遍历 $this->clients 发送。
关键限制:
-
$this->clients是SplObjectStorage,只存当前进程内的连接,跨进程或重启后清空 - 别在
onMessage里做耗时操作(如查大表、调外部 API),否则阻塞整个事件循环;应丢给队列或异步处理 - 前端连接 URL 必须带鉴权参数(如
token),Ratchet 不信任任何来自 CI 的会话上下文
最易被忽略的一点:Ratchet 进程没有请求上下文,也没有 CI 的自动加载、日志、配置机制。所有数据库连接、缓存客户端、加密密钥都得手动初始化,且要确保进程长期运行时不泄漏资源或累积内存。一旦忘记关闭 PDO 连接或未 unset 大对象,几天后服务就会因内存溢出静默崩溃。











