You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何在CodeIgniter与Ratchet PHP WebSocket间共享会话变量以识别连接用户?

当然可以实现!要在CodeIgniter和Ratchet WebSocket之间共享会话、识别连接用户,核心是让两者能访问同一份会话数据,同时避开异步环境下PHP原生会话的锁阻塞问题。下面是几种经过验证的方案:

方案1:利用CodeIgniter的数据库会话存储

如果你的CodeIgniter已经配置了数据库存储会话(这是最常见的场景),可以让Ratchet直接查询数据库中的会话记录来获取用户身份:

步骤1:确认CodeIgniter的会话配置

先确保CodeIgniter使用数据库会话驱动,在application/config/config.php中设置:

$config['sess_driver'] = 'database';
$config['sess_save_path'] = 'ci_sessions'; // 你的会话表名
$config['sess_encrypt_cookie'] = true; // 如果开启了加密,后续要对应处理
$config['encryption_key'] = 'your_encryption_key_here'; // 你的加密密钥

会话表的结构可以参考CodeIgniter官方文档生成。

步骤2:在Ratchet中解析并验证会话

在你的Ratchet组件类中,解析客户端Cookie中的CodeIgniter会话ID,然后查询数据库解密会话数据:

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use PDO;

class UserAwareWebSocket implements MessageComponentInterface {
    protected $clients;
    protected $db;
    private $encryptionKey;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
        // 连接到CodeIgniter使用的同一个数据库
        $this->db = new PDO('mysql:host=localhost;dbname=your_ci_db', 'db_user', 'db_pass');
        // 复制CodeIgniter的加密密钥
        $this->encryptionKey = 'your_encryption_key_here';
    }

    public function onOpen(ConnectionInterface $conn) {
        // 从请求Cookie中获取CodeIgniter会话ID
        $cookies = $conn->WebSocket->request->getCookies();
        if (!isset($cookies['ci_session'])) {
            $conn->close(); // 无会话,拒绝连接
            return;
        }

        $sessionId = $cookies['ci_session'];
        // 查询有效会话(未过期)
        $stmt = $this->db->prepare("SELECT data FROM ci_sessions WHERE id = ? AND expires > UNIX_TIMESTAMP()");
        $stmt->execute([$sessionId]);
        $encryptedSession = $stmt->fetchColumn();

        if (!$encryptedSession) {
            $conn->close(); // 会话过期或不存在,拒绝连接
            return;
        }

        // 解密会话数据(和CodeIgniter的加密逻辑一致)
        $decoded = base64_decode($encryptedSession);
        $ivLength = openssl_cipher_iv_length('AES-256-CBC');
        $iv = substr($decoded, 0, $ivLength);
        $encryptedData = substr($decoded, $ivLength);
        $decryptedData = openssl_decrypt($encryptedData, 'AES-256-CBC', $this->encryptionKey, OPENSSL_RAW_DATA, $iv);
        $session = unserialize($decryptedData);

        // 获取用户ID(假设你的会话中存储了user_id字段)
        if (isset($session['user_id'])) {
            $conn->user_id = $session['user_id'];
            echo "用户 {$session['user_id']} 已连接\n";
            $this->clients->attach($conn);
        } else {
            $conn->close(); // 未登录用户,拒绝连接
        }
    }

    // 实现onMessage、onClose、onError方法...
    public function onMessage(ConnectionInterface $from, $msg) {
        // 可以使用$from->user_id识别发送消息的用户
        echo "用户 {$from->user_id} 发送消息:{$msg}\n";
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        if (isset($conn->user_id)) {
            echo "用户 {$conn->user_id} 已断开连接\n";
        }
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
    }
}
方案2:使用Redis作为共享会话存储

如果你的项目已经在使用Redis,这是更优的方案——Redis天生适合异步环境,无锁且性能高:

步骤1:配置CodeIgniter使用Redis会话

在application/config/config.php中设置:

$config['sess_driver'] = 'redis';
$config['sess_save_path'] = 'tcp://localhost:6379'; // 你的Redis地址
$config['sess_encrypt_cookie'] = false; // 可选,若开启加密仍需解密

步骤2:在Ratchet中连接Redis获取会话

使用predis/predis库连接Redis,直接读取会话数据:

use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Predis\Client;

class RedisWebSocket implements MessageComponentInterface {
    protected $clients;
    protected $redis;

    public function __construct() {
        $this->clients = new \SplObjectStorage;
        // 连接到同一个Redis实例
        $this->redis = new Client('tcp://localhost:6379');
    }

    public function onOpen(ConnectionInterface $conn) {
        $cookies = $conn->WebSocket->request->getCookies();
        if (!isset($cookies['ci_session'])) {
            $conn->close();
            return;
        }

        $sessionId = $cookies['ci_session'];
        // 从Redis中获取会话数据(CodeIgniter的Redis会话键格式为:ci_session:<session_id>)
        $sessionData = $this->redis->get("ci_session:{$sessionId}");

        if (!$sessionData) {
            $conn->close();
            return;
        }

        // 反序列化会话数据(若未加密)
        $session = unserialize($sessionData);
        if (isset($session['user_id'])) {
            $conn->user_id = $session['user_id'];
            $this->clients->attach($conn);
            echo "用户 {$session['user_id']} 已连接\n";
        } else {
            $conn->close();
        }
    }

    // 其他方法实现...
}
关键注意事项
  • Cookie传递:前端连接WebSocket时,必须确保Cookie被发送。如果是浏览器环境,要设置WebSocket实例的withCredentials: true,同时注意跨域场景下的CookieSameSite和Secure属性配置。
  • 会话一致性:确保Ratchet中的加密密钥、会话存储配置和CodeIgniter完全一致,否则会出现解密失败或会话无法读取的问题。
  • 异步环境限制:绝对不要在Ratchet代码中使用session_start(),因为PHP原生会话是阻塞式的,会导致WebSocket服务卡死。
  • 会话过期处理:查询会话时一定要检查过期时间,避免使用已失效的会话数据。

内容的提问来源于stack exchange,提问作者authentic972

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.05.19 10:00:22