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

如何从Ratchet WebSocket获取带用户ID的已连接用户列表?

问题分析

执行test.php时出现端口占用错误,核心原因有两个:

  1. 你在test.php中require_once('websocket.php'),而websocket.php末尾会执行$server->run(),尝试再次绑定20081端口,但该端口已被运行中的WebSocket服务器占用,因此抛出EADDRINUSE错误。
  2. 即使端口不冲突,外部脚本也无法直接访问运行中WebSocket进程的内存对象($webSocketServer属于独立进程,和test.php进程内存隔离)。

解决方法

1. 重构WebSocket服务器代码,避免被外部脚本重复启动

修改websocket.php,仅在命令行执行时启动服务器(因为WebSocket服务器本身就是命令行常驻进程):

<?php

require_once("/home/bitnami/vendor/autoload.php");
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;

class WebSocketHandler implements MessageComponentInterface {
    private $clients;
    // 新增Redis实例,用于存储在线用户(需先安装predis/predis:composer require predis/predis)
    private $redis;

    public function __construct() {
        $this->clients = new \SplObjectStorage();
        // 初始化Redis连接
        $this->redis = new \Predis\Client([
            'scheme' => 'tcp',
            'host'   => '127.0.0.1',
            'port'   => 6379,
        ]);
    }

    public function onOpen(ConnectionInterface $conn) {
        $request = $conn->httpRequest;
        $action = $request->getHeaderLine("action");
        if ($action === "addClient") {
            $userId = $request->getHeaderLine('userId');            
            $conn->resourceId = $userId;
            $this->clients->attach($conn, $userId);
            // 将用户ID存入Redis集合
            $this->redis->sAdd('online_users', $userId);
            file_put_contents('zee1.log', print_r($userId.' '.$action, true));
        }
    }

    public function onClose(ConnectionInterface $conn) {
        $this->clients->detach($conn);
        // 用户断开时从Redis移除
        if (isset($conn->resourceId)) {
            $this->redis->sRem('online_users', $conn->resourceId);
        }
    }

    public function onError(ConnectionInterface $conn, \Exception $e) {
        $conn->close();
        // 出错时从Redis移除用户
        if (isset($conn->resourceId)) {
            $this->redis->sRem('online_users', $conn->resourceId);
        }
    }
    
    private function findClientConnection($userId) {
        foreach ($this->clients as $client) {
            if ($client->resourceId === $userId) {
                return $client;
            }
        }
        return null;
    }

    public function getClients(){
        $connectedClients = [];
        foreach ($this->clients as $client) {
            $connectedClients[$client->resourceId] = $client;
        }
        return $connectedClients;
    }   
}

// 仅命令行执行时启动服务器
if (php_sapi_name() === 'cli') {
    $webSocketServer = new WebSocketHandler();

    $server = IoServer::factory(
        new HttpServer(
            new WsServer(
                $webSocketServer
            )
        ),
        20081
    );

    $server->run();
}

2. 修改test.php,通过Redis获取在线用户列表

直接连接Redis读取在线用户集合,无需依赖WebSocket服务器代码:

<?php 
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// 连接Redis
$redis = new \Predis\Client([
    'scheme' => 'tcp',
    'host'   => '127.0.0.1',
    'port'   => 6379,
]);

// 获取在线用户ID列表
$onlineUsers = $redis->sMembers('online_users');

echo "在线用户ID列表:\n";
foreach ($onlineUsers as $userId) {
    echo "User ID: $userId\n";
}
?>

备选方案:给WebSocket服务器添加HTTP查询接口

如果不想用Redis,可以在WebSocket服务器的事件循环中新增一个HTTP服务,用于外部查询在线用户:
修改websocket.php的服务器启动部分:

if (php_sapi_name() === 'cli') {
    $webSocketServer = new WebSocketHandler();

    // 创建WebSocket服务器
    $wsServer = IoServer::factory(
        new HttpServer(
            new WsServer(
                $webSocketServer
            )
        ),
        20081
    );

    // 获取React事件循环
    $loop = $wsServer->loop;

    // 创建HTTP查询服务,监听20082端口
    $httpServer = new \React\Http\HttpServer(function (\Psr\Http\Message\ServerRequestInterface $request) use ($webSocketServer) {
        $clients = $webSocketServer->getClients();
        $userIds = array_keys($clients);
        return new \React\Http\Message\Response(
            200,
            ['Content-Type' => 'application/json'],
            json_encode(['online_users' => $userIds])
        );
    });

    $httpServer->listen(new \React\Socket\SocketServer('0.0.0.0:20082'), $loop);

    $wsServer->run();
}

然后test.php通过HTTP请求获取:

<?php 
ini_set('display_errors', 1);
ini_set('display_startup_errors', 1);
error_reporting(E_ALL);

// 调用HTTP接口获取在线用户
$response = file_get_contents('http://127.0.0.1:20082');
$onlineUsers = json_decode($response, true)['online_users'];

echo "在线用户ID列表:\n";
foreach ($onlineUsers as $userId) {
    echo "User ID: $userId\n";
}
?>

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

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.07.19 02:14:55