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