swoole websocket 使用静态变量不能存放所有连接标识fd

<?php

use Swoole\WebSocket\Server;

class WebSocketServer
{
    protected static $clients = [];

    public function __construct()
    {
        $server = new Server("127.0.0.1", 9501);

        // 每次请求都创建新的实例
        $server->on('open', [$this, 'onOpen']);
        $server->on('message', [$this, 'onMessage']);
        $server->on('close', [$this, 'onClose']);

        $server->start();
    }

    public function onOpen($server, $request)
    {
        self::$clients[$request->fd] = $request->fd;
    }

    public function onMessage($server, $frame)
    {
        // Handle messages...
    }

    public function onClose($server, $fd)
    {
        unset(self::$clients[$fd]);
    }
}

// 每次请求都会创建一个新实例
new WebSocketServer();

因为 Swoole 服务器默认开启多个 Worker 进程(根据cpu核数决定),静态变量是 每个进程独立的。即使 self::$clients 在一个 Worker 进程中存储了客户端连接的 fd,但是在其他 Worker 进程中,它是空的。

解决方法: 为了在多个 Worker 进程之间共享客户端连接数据,可以使用 Swoole 提供的 共享内存Redis 等方式。Swoole 的多进程架构意味着静态变量在每个 Worker 进程中是独立的。

使用 Swoole 的 Swoole\Table 实现跨进程共享数据

<?php

use Swoole\WebSocket\Server;
use Swoole\Table;

class WebSocketServer
{
    protected $clientsTable;

    public function __construct()
    {
        // 使用 Swoole\Table 存储客户端信息(跨 Worker 进程共享)
        $this->clientsTable = new Table(1024);
        $this->clientsTable->column('fd', Table::TYPE_INT);
        $this->clientsTable->column('user_id', Table::TYPE_INT);
        $this->clientsTable->create();

        $server = new Server("127.0.0.1", 9501);

        $server->on('open', [$this, 'onOpen']);
        $server->on('message', [$this, 'onMessage']);
        $server->on('close', [$this, 'onClose']);

        $server->start();
    }

    // 处理客户端连接
    public function onOpen($server, $request)
    {
        echo "New connection: {$request->fd}\n";
        $this->clientsTable->set($request->fd, ['fd' => $request->fd]);
        var_dump($this->clientsTable->get($request->fd));
    }

    // 处理客户端消息
    public function onMessage($server, $frame)
    {
        echo "Received message: {$frame->data} from client: {$frame->fd}\n";

        // 广播消息给所有客户端
        foreach ($this->clientsTable as $fd => $client) {
            if ($fd != $frame->fd) {
                $server->push($fd, "Broadcast message: {$frame->data}");
            }
        }
    }

    // 处理客户端断开
    public function onClose($server, $fd)
    {
        echo "Connection closed: {$fd}\n";
        $this->clientsTable->del($fd);
        var_dump($this->clientsTable->get($fd));
    }
}

// 创建 WebSocketServer 实例并启动
$server = new WebSocketServer();

Leave a Reply

Your email address will not be published. Required fields are marked *.

*
*

en_USEnglish
Powered by TranslatePress