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();