codeigniter 3.x 不支持 websocket,因控制器依赖 php-fpm 生命周期,而 websocket 需常驻进程;必须用 ratchet 独立启动 cli 服务,通过 redis 共享状态并手动管理连接与鉴权。

CodeIgniter 3.x 本身不支持 WebSocket,任何试图在 Chat.php 控制器里写 onOpen() 或直接 new WsServer 的做法都会失败——连接握手后几秒内必然断开,onMessage() 永远不会触发。
为什么不能在 CI 控制器里启动 WebSocket 服务
WebSocket 握手虽用 HTTP GET + Upgrade: websocket 头发起,但后续通信完全脱离 PHP-FPM 生命周期。CI 的控制器由 Apache/Nginx 调用,请求一结束,整个进程(含所有对象实例)即被销毁。ConnectionInterface 实例随之消失,Ratchet 的事件循环根本无法持续运行。
常见错误现象:
- 浏览器控制台显示
WebSocket connection to 'ws://localhost:8080/' failed - 服务端日志刷出
New connection! (123),但无后续onMessage日志 - 用
ps aux | grep php查不到常驻的chat_server.php进程
必须用 Ratchet 启动独立常驻进程
Ratchet 不是 CI 插件,而是一个独立的 PHP WebSocket 服务框架。它需绕过 CI 入口(index.php),用 CLI 方式长期运行,与 CI 应用并行,仅通过共享存储交换数据。
实操要点:
- 在项目根目录新建
chat_server.php,不放在application/下 - 用原生 PDO 或
new Redis()连接数据库/缓存,不要调用$this->ci->load->model()(get_instance()在 CLI 环境下返回null) - 启动命令必须带
php前缀:php chat_server.php,不能用php -S(那是内置 HTTP 服务器) - 绑定地址建议用
0.0.0.0:8080而非127.0.0.1:8080,避免 Docker 或跨域调试时连不上
CI 与 Ratchet 如何共享用户身份和在线状态
二者没有内存或会话共享,必须设计轻量级状态同步机制。推荐用 Redis 存储临时 token 映射,比查数据库更快也更可靠。
关键步骤:
- 用户登录成功后,CI 写入:
SET ws:token:abc123 "{"user_id":5,"expires":1747000000}",并设 TTL(如 3600 秒) - Ratchet 在
onOpen()中解析 URL 参数?token=abc123,用$redis->get('ws:token:abc123')获取用户 ID - 验证通过后,将
$conn->resourceId与user_id写入 Redis Hash:HSET ws:online 123 5 - 广播消息前,先
HVALS ws:online拿到所有在线user_id,再查表得对应resourceId列表,逐个$conn->send()
前端连接与鉴权不能依赖 CI Session
浏览器 WebSocket 连接(new WebSocket('ws://...?token=...'))发生在 HTTP 请求之外,$_SESSION 和 CI 的 $this->session 完全不可见。任何基于 Cookie 的自动鉴权都无效。
必须显式传递凭证:
- 登录接口返回一个短期有效的
token字段(非 session_id),前端存入localStorage - 建立 WebSocket 连接时手动拼 URL:
new WebSocket('ws://localhost:8080?token=' + token) - Ratchet 端不做信任假设:每个
onMessage都要校验该resourceId是否仍存在于ws:online中,防止伪造连接 ID - 前端收到
close事件后,主动清除本地 token 缓存,避免重连时用失效凭证
最易被忽略的是:Ratchet 进程里所有数据库/缓存操作都必须自己初始化,CI 的自动加载、配置、钩子全部失效;一旦你写了 $this->db->insert() 或 $this->redis->lPush(),就等于默认它已存在——而这在 CLI 环境下从来不会自动发生。











