fage1151 / socketio
Socket.IO v4 compatible server based on Workerman
Requires
- php: >=8.1
- psr/log: ^3.0
- workerman/workerman: ^4.1 || ^5.0
Requires (Dev)
- phpstan/phpstan: ^1.10
- phpunit/phpunit: ^10.0 || ^11.0
- squizlabs/php_codesniffer: ^3.7
Suggests
- workerman/channel: 使用 ClusterAdapter 进行集群通信
- workerman/redis: 使用 RedisAdapter 进行集群通信
This package is not auto-updated.
Last update: 2026-05-04 16:55:39 UTC
README
Socket.IO 的 PHP 服务器实现,支持 WebSocket 和 Long-Polling 传输,完整兼容 Socket.IO 协议 v4。
基于 Workerman 实现的高性能、生产级别的 Socket.IO 服务器,专为 PHP 环境优化。
特性
- 多传输协议支持:支持 WebSocket 和 HTTP Long-Polling(多进程模式下仅支持 WebSocket,且需要显式使用)
- 二进制数据支持:支持二进制事件传输
- 集群支持:通过 Channel 或 Redis 实现多进程集群通信
- 房间管理:支持房间(Room)管理功能
- 命名空间:支持多个命名空间,推荐使用 $io->of() 方法
- 事件确认(ACK):支持双向 ACK 确认机制,强制使用 callback 回复
- 三层中间件系统:支持全局、命名空间级别和 Socket 实例级别的中间件
- 连接恢复机制:完整支持 Socket.IO v4 连接状态恢复(private id, offset tracking, state recovery)
- 心跳检测:自动心跳保活机制
- PSR-3 日志系统:支持 PSR-3 标准日志接口
- PHP 8.1+ 优化实现:使用 PHP 8.1 最新特性,性能优化
系统要求
- PHP >= 8.1
- Workerman >= 4.0
- psr/log >= 3.0
- 可选依赖:
- workerman/channel(使用 ClusterAdapter 时需要)
- workerman/redis(使用 RedisAdapter 时需要)
安装
使用 Composer(推荐)
composer require fage1151/socketio
从 GitHub 克隆
# 克隆项目
git clone https://github.com/phpsocketio/socket.io.git
# 进入项目目录
cd socket.io
# 安装依赖
composer install
安装可选依赖
使用 ClusterAdapter 时:
ClusterAdapter 基于 Workerman Channel,默认不包含,需要安装:
composer require workerman/channel
使用 RedisAdapter 时:
composer require workerman/redis
项目结构
├── src/ # 核心源码目录
│ ├── Adapter/ # 适配器目录
│ │ ├── AdapterInterface.php # 适配器接口(含跨进程方法)
│ │ ├── ClusterAdapter.php # 基于Channel的集群适配器
│ │ └── RedisAdapter.php # 基于Redis的集群适配器
│ ├── Protocol/ # 协议相关
│ │ ├── PacketParser.php # 数据包解析器
│ │ ├── PacketDispatcher.php # 数据包分发器
│ │ └── EngineIOHandler.php # Engine.IO协议处理器
│ ├── Transport/ # 传输层
│ │ ├── AbstractTransportHandler.php # 传输层基类
│ │ ├── WebSocketHandler.php # WebSocket处理器
│ │ ├── PollingHandler.php # 轮询处理器
│ │ └── ConnectionManager.php # 连接管理器
│ ├── Room/ # 房间管理
│ │ └── RoomManager.php # 房间管理器
│ ├── Event/ # 事件系统
│ │ ├── EventHandler.php # 事件处理器
│ │ └── AckManager.php # ACK回调管理器
│ ├── Session/ # 会话相关
│ │ ├── SessionStore.php # 会话存储管理器
│ │ └── RecoveryManager.php # 连接状态恢复管理器
│ ├── Enum/ # 枚举类型
│ │ ├── EnginePacketType.php # Engine.IO数据包类型枚举
│ │ ├── SocketPacketType.php # Socket.IO数据包类型枚举
│ │ └── LogLevelPriority.php # 日志级别优先级枚举
│ ├── Exceptions/ # 异常类
│ │ └── SocketIOException.php # Socket.IO统一异常类
│ ├── Support/ # 支持类
│ │ ├── Logger.php # PSR-3 兼容日志器
│ │ ├── ErrorHandler.php # 错误处理器
│ │ ├── Set.php # 集合数据结构
│ │ ├── ServerConfig.php # 服务器配置类,管理配置
│ │ ├── ServerBuilder.php # 服务器构建器,简化服务器创建
│ │ └── ServerManager.php # 服务器管理器
│ ├── SocketIOServer.php # Socket.IO服务器主类
│ ├── Socket.php # Socket类
│ ├── SocketEventEmitter.php # Socket事件发射器Trait
│ ├── SocketNamespace.php # 命名空间处理类
│ ├── Session.php # 会话管理
│ └── Broadcaster.php # 统一广播器
├── examples/ # 示例目录
│ └── server.php # 完整服务器示例
├── docs/ # 文档目录
│ ├── API.md # API参考文档
│ └── USAGE.md # 详细使用文档
├── tests/ # 测试目录
├── README.md # 项目说明文档(中文)
├── README.en.md # 项目说明文档(英文)
├── composer.json # Composer配置
├── phpstan.neon # PHPStan配置
├── phpcs.xml # PHPCS配置
└── LICENSE # 许可证文件
核心文件说明
核心类(根目录)
- src/SocketIOServer.php:Socket.IO 服务器主类,负责处理连接和事件分发
- src/SocketNamespace.php:命名空间处理类,通过 $io->of() 访问
- src/Session.php:会话管理,管理客户端会话状态和连接恢复
- src/Socket.php:Socket 类,封装客户端连接接口
- src/SocketEventEmitter.php:Socket事件发射器Trait,封装Socket事件处理功能
- src/Broadcaster.php:统一广播器,负责消息广播
协议相关(Protocol/)
- src/Protocol/PacketParser.php:数据包解析器,解析和构建 Socket.IO 数据包(使用静态映射性能优化)
- src/Protocol/PacketDispatcher.php:数据包分发器,处理Socket.IO包的分发
- src/Protocol/EngineIOHandler.php:Engine.IO 协议处理器,处理底层传输协议(使用回调解耦)
传输层(Transport/)
- src/Transport/AbstractTransportHandler.php:传输层基类,包含通用的 IP 提取和握手数据构建
- src/Transport/WebSocketHandler.php:WebSocket 处理器,处理 HTTP 轮询和 WebSocket 握手
- src/Transport/PollingHandler.php:轮询处理器
- src/Transport/ConnectionManager.php:连接管理器
房间管理(Room/)
- src/Room/RoomManager.php:房间管理器,处理房间相关操作
事件系统(Event/)
- src/Event/EventHandler.php:事件处理器,负责处理各种 Socket.IO 事件
- src/Event/AckManager.php:ACK回调管理器,管理ack回调(统一管理所有ACK)
适配器(Adapter/)
- src/Adapter/AdapterInterface.php:适配器接口,定义跨进程通信标准
- src/Adapter/RedisAdapter.php:Redis 适配器,支持跨进程房间和会话管理
- src/Adapter/ClusterAdapter.php:Channel 适配器,支持跨进程房间和会话管理
AdapterInterface 跨进程方法
| 方法 | 说明 |
|---|---|
allSockets(array $rooms = []) | 获取所有连接的 Socket ID 集合(跨进程) |
fetchSockets(array $rooms = []) | 获取所有 Socket 实例信息(跨进程) |
socketsJoin(string\|array $rooms, array $targetSockets = []) | 让指定 Socket 加入房间(跨进程) |
socketsLeave(string\|array $rooms, array $targetSockets = []) | 让指定 Socket 离开房间(跨进程) |
disconnectSockets(bool $close = false, array $targetSockets = []) | 断开指定 Socket 连接(跨进程) |
serverSideEmit(string $eventName, array $args = [], ?callable $ack = null) | 向集群中其他服务器发送消息 |
枚举类型(Enum/)
- src/Enum/EnginePacketType.php:Engine.IO 数据包类型枚举
- src/Enum/SocketPacketType.php:Socket.IO 数据包类型枚举
- src/Enum/LogLevelPriority.php:日志级别优先级枚举
异常类(Exceptions/)
- src/Exceptions/SocketIOException.php:Socket.IO统一异常类(提供静态方法创建不同类型异常)
会话相关(Session/)
- src/Session/SessionStore.php:会话存储管理器,专门管理会话存储和缓存
- src/Session/RecoveryManager.php:连接状态恢复管理器,处理重连状态恢复
支持类(Support/)
- src/Support/Logger.php:PSR-3 兼容日志器
- src/Support/ErrorHandler.php:错误处理器
- src/Support/Set.php:集合数据结构(使用关联数组O(1)查找性能优化)
- src/Support/ServerConfig.php:服务器配置类,单一职责管理配置
- src/Support/ServerBuilder.php:服务器构建器,流畅 API 简化服务器创建
- src/Support/ServerManager.php:服务器管理器,管理生命周期和适配器
架构优化说明
重构目标
项目经过多次优化重构,实现了:
- 高内聚:每个类职责清晰单一
- 低耦合:组件间依赖关系简化
- 低冗余:消除了重复代码
- 高性能:多处性能优化
- 易维护:代码结构清晰,PHPStan 0 错误
主要重构工作
- 从SocketIOServer提取PacketDispatcher:专门处理数据包分发
- 从EventHandler提取AckManager:统一管理所有ACK回调
- 合并SocketConn到Socket:消除冗余包装类
- 从Session提取SessionStore和RecoveryManager:分离静态存储和恢复逻辑独立
- 从Socket提取SocketEventEmitter Trait:降低Socket类复杂度
- 移除双重分派:EventHandler不再需要再进行双重路由,直接使用PacketDispatcher
- Set性能优化:从O(n)查找改为O(1)查找
- PacketParser静态映射:性能优化
- 异常类合并:3个异常类合并为1个,使用静态工厂方法
- Broadcaster链式调用优化:使用clone替代直接调用,减少构造器开销
- EngineIOHandler解耦:使用回调替代直接依赖EventHandler和RoomManager
- fetchSockets简化:使用更简洁if替代复杂闭包
- initializeMaps方法优化:显式初始化静态映射
- 移除AckManager中ReflectionFunction调用:减少不必要的性能开销
- Adapter跨进程方法补充:新增allSockets、fetchSockets、socketsJoin、socketsLeave、disconnectSockets方法
性能提升
- Set类查找性能从O(n)提升至O(1)
- PacketParser类型查找不再遍历枚举改为静态映射
- 消除了多处冗余对象构造函数减少开销
- 移除了多处重复路由,简化ACK
- ACK回调管理统一,减少Session与AckManager间的重复
- Adapter支持完整的跨进程操作
快速开始
基本用法
use Workerman\Worker;
use PhpSocketIO\SocketIOServer;
use Psr\Log\LogLevel;
// 创建 Socket.IO 服务器实例
$io = new SocketIOServer('0.0.0.0:8088', [
'pingInterval' => 25000, // 心跳间隔(毫秒)
'pingTimeout' => 20000, // 心跳超时(毫秒)
'maxPayload' => 10485760, // 最大负载(字节)
'workerCount' => 1, // worker 数量,默认为 1
'logLevel' => LogLevel::INFO, // 日志级别
]);
// 使用 $io->of() 注册命名空间的连接事件处理
$io->of('/chat')->on('connection', function ($socket) use ($io) {
// 检查是否是重连恢复的连接
if ($socket->recovered) {
echo "连接已恢复,继续使用之前的数据!\n";
// $socket->data 和房间信息已自动恢复
// 可以重发可能丢失的消息
}
// 发送欢迎消息
$socket->emit('welcome', 'Welcome to Socket.IO server!');
// 设置 socket 的自定义数据(断连时会保存,重连时会恢复)
$socket->data['userId'] = 'user_' . time();
$socket->data['name'] = 'Guest';
// 加入房间(断连时会保存,重连时会自动恢复)
$socket->join('welcome');
// 聊天消息处理
$socket->on('chat message', function ($msg) use ($socket) {
$socket->broadcast->emit('chat message', $msg);
});
// ACK 消息处理 - 强制使用 callback,不要使用 return
$socket->on('ack', function ($msg, $callback = null) use ($socket) {
if (is_callable($callback)) {
$callback(['status' => 'ok', 'data' => $msg]);
}
});
// 断开连接处理
$socket->on('disconnect', function () use ($socket) {
// 清理逻辑(注意:断开时会自动保存状态用于恢复)
});
});
// 启动 Workerman
Worker::runAll();
使用 ServerBuilder 流畅 API
use Workerman\Worker;
use PhpSocketIO\Support\ServerBuilder;
use Psr\Log\LogLevel;
// 使用 ServerBuilder 流畅 API 创建服务器
$io = ServerBuilder::create()
->listen('0.0.0.0:8088')
->pingInterval(25000)
->pingTimeout(20000)
->workerCount(1)
->logLevel(LogLevel::INFO)
->cors('*')
->build();
// 后续操作和普通方式一样
$io->of('/chat')->on('connection', function ($socket) {
$socket->emit('welcome', 'Welcome!');
});
Worker::runAll();
使用日志
use Psr\Log\LogLevel;
// 1. 使用内置日志器(默认)
$io = new SocketIOServer('0.0.0.0:8088', [
'logLevel' => LogLevel::DEBUG
]);
// 2. 设置自定义日志处理器
$io->getLogger()->setHandler(function ($level, $message, $context) {
// 这里可以写入文件、数据库、或者其他日志服务
file_put_contents('/path/to/logs/socketio.log',
"[{$level}] {$message}\n", FILE_APPEND
);
});
// 3. 使用第三方 PSR-3 日志库,如 Monolog
$logger = new \Monolog\Logger('socketio');
$logger->pushHandler(new \Monolog\Handler\StreamHandler('/path/to/logs/socketio.log'));
$io->setLogger($logger);
使用中间件
// 1. 全局中间件 - 作用于所有命名空间和事件
$io->use(function ($socket, $packet, $next) {
$sid = $socket['id'] ?? 'unknown';
echo "[Global Middleware] SID: {$sid}\n";
$next();
});
// 2. 命名空间级中间件 - 只作用于 /chat 命名空间
$io->of('/chat')->use(function ($socket, $packet, $next) {
echo "[Namespace Middleware] /chat\n";
$next();
});
// 3. Socket 实例级中间件 - 只作用于当前连接
$io->of('/chat')->on('connection', function ($socket) {
$socket->use(function ($packet, $next) use ($socket) {
echo "[Socket Middleware] Socket {$socket->id}\n";
$next();
});
});
多 worker 配置示例
当需要使用多个 worker 进程时,必须通过 setAdapter 方法设置 adapter:
使用 ClusterAdapter(基于 Workerman Channel)
注意:使用 ClusterAdapter 需要先安装 workerman/channel:
composer require workerman/channel
use Workerman\Worker;
use PhpSocketIO\SocketIOServer;
use PhpSocketIO\Adapter\ClusterAdapter;
// 创建多 worker 服务器实例(4个 worker)
$io = new SocketIOServer('0.0.0.0:8088', [
'pingInterval' => 25000,
'pingTimeout' => 20000,
'workerCount' => 4, // 设置 4 个 worker
]);
// 创建并设置集群适配器
$adapter = new ClusterAdapter([
'channel_ip' => '127.0.0.1',
'channel_port' => 2206,
'prefix' => 'socketio_',
'heartbeat' => 25
]);
$io->setAdapter($adapter);
// 事件处理代码...
$io->of('/chat')->on('connection', function ($socket) {
// 处理连接...
});
Worker::runAll();
使用 RedisAdapter(基于 Redis)
注意:使用 RedisAdapter 需要先安装 workerman/redis:
composer require workerman/redis
use Workerman\Worker;
use PhpSocketIO\SocketIOServer;
use PhpSocketIO\Adapter\RedisAdapter;
// 创建多 worker 服务器实例(4个 worker)
$io = new SocketIOServer('0.0.0.0:8088', [
'pingInterval' => 25000,
'pingTimeout' => 20000,
'workerCount' => 4, // 设置 4 个 worker
]);
// 创建并设置 Redis 适配器
$adapter = new RedisAdapter([
'host' => '127.0.0.1',
'port' => 6379,
'auth' => null, // Redis 认证密码,无密码时为 null
'db' => 0, // Redis 数据库编号
'prefix' => 'socketio_',
'heartbeat' => 25
]);
$io->setAdapter($adapter);
// 事件处理代码...
$io->of('/chat')->on('connection', function ($socket) {
// 处理连接...
});
Worker::runAll();
房间操作
$io->of('/chat')->on('connection', function ($socket) use ($io) {
// 加入房间
$socket->join('room1');
// 离开房间
$socket->leave('room1');
// 只向指定房间发送
$io->of('/chat')->to('room1')->emit('some event', 'message');
// 向多个房间发送
$io->of('/chat')->to('room1')->to('room2')->emit('some event', 'message');
// 广播(排除当前 socket)
$socket->broadcast->emit('some event', 'message');
// 在指定房间内广播
$socket->to('room1')->emit('some event', 'message');
// 排除特定房间
$socket->except('room2')->emit('some event', 'message');
});
事件确认(ACK)
重要:ACK 回复必须使用 callback,不要使用 return!
$io->of('/chat')->on('connection', function ($socket) {
$socket->on('reqAck', function ($data, $callback = null) {
// 处理数据
$result = ['status' => 'ok', 'data' => $data];
// 调用 callback 发送确认
if (is_callable($callback)) {
$callback($result);
}
});
// 服务器发送带 ACK 的消息给客户端
$socket->on('ping', function () use ($socket) {
$socket->emitWithAck('ackResponse', 'Hello', function ($clientData) {
// 处理客户端回复
});
});
});
连接恢复机制
本项目完整实现了 Socket.IO v4 连接状态恢复机制,当客户端网络不稳定导致断连后,可以恢复之前的会话状态。
特性
- 自动保存状态:断开连接时自动保存房间信息、自定义数据和已发送消息
- 状态恢复:重连时自动恢复房间和自定义数据
- 丢失消息重发:通过 offset 跟踪,自动重发可能丢失的消息
- 简便使用:通过
$socket->recovered属性检查连接是否成功恢复
使用示例
$io->of('/chat')->on('connection', function ($socket) use ($io) {
// 检查连接是否已恢复
if ($socket->recovered) {
echo "用户已恢复连接!\n";
// $socket->data 和房间信息已自动恢复
// 可以检查 $socket->data 中是否有需要续传的数据
} else {
echo "新用户连接!\n";
// 新连接,初始化数据
$socket->data['username'] = 'Guest_' . mt_rand();
}
// 设置自定义数据(断连后会保存,重连后会恢复)
$socket->data['lastActive'] = time();
// 加入房间(断连后会保存,重连后会自动恢复)
$socket->join('chat_room');
// 发送消息(offset 会自动跟踪)
$socket->emit('system_message', '欢迎来到聊天室!');
});
客户端配置
客户端需要启用重连机制(Socket.IO 客户端默认已启用):
const io = require('socket.io-client');
// 连接服务器(默认重连已启用)
const socket = io('http://localhost:8088/chat', {
// 可选:自定义重连参数
reconnection: true,
reconnectionDelay: 100,
reconnectionDelayMax: 500,
reconnectionAttempts: 10,
});
工作原理
- 连接建立:服务器为每个连接生成唯一的
pid(private id) - 消息跟踪:每个发送的事件都带有
offset标记 - 断连保存:连接断开时,服务器保存:
- 当前连接的所有房间
$socket->data中的自定义数据- 最近发送的事件列表(带 offset)
- 重连恢复:
- 客户端重连时发送之前的
pid和最新的offset - 服务器验证 pid 是否有效且未超时
- 自动恢复房间信息和自定义数据
- 重发 offset 之后可能丢失的消息
- 设置
$socket->recovered = true
- 客户端重连时发送之前的
- 过期清理:保存的状态 120 秒后自动过期
配置选项
连接恢复机制内置在服务器中,无需额外配置。以下是默认参数:
- 恢复超时:120 秒(超过此时间的状态无法恢复)
- 最大保存消息数:1000 条(最多保存的事件数量)
配置选项
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
pingInterval | int | 25000 | 心跳间隔(毫秒) |
pingTimeout | int | 20000 | 心跳超时(毫秒) |
maxPayload | int | 10485760 | 最大负载大小(字节) |
workerCount | int | 1 | Worker 进程数量 |
logLevel | string | LogLevel::INFO | 日志级别(PSR-3) |
ssl | array | [] | SSL 配置(用于 HTTPS/WSS) |
cors | array/string | null | CORS 跨域配置 |
CORS 配置
支持以下几种配置方式:
// 方式1:简单配置,仅指定允许的源
$io = new SocketIOServer('0.0.0.0:8088', [
'cors' => 'https://example.com'
]);
// 方式2:完整配置
$io = new SocketIOServer('0.0.0.0:8088', [
'cors' => [
'origin' => 'https://example.com',
'methods' => ['GET', 'POST', 'OPTIONS'],
'allowedHeaders' => ['my-custom-header', 'Content-Type'],
'credentials' => true
]
]);
// 方式3:允许多个源(需要动态处理)
$io = new SocketIOServer('0.0.0.0:8088', [
'cors' => [
'origin' => ['https://example.com', 'https://app.example.com'],
'methods' => ['GET', 'POST', 'OPTIONS'],
'allowedHeaders' => ['Content-Type', 'Authorization'],
'credentials' => true
]
]);
CORS 配置选项说明
| 选项 | 类型 | 默认值 | 描述 |
|---|---|---|---|
origin | string/array | * | 允许的源,可以是字符串或数组 |
methods | array | ['GET', 'POST', 'OPTIONS'] | 允许的 HTTP 方法 |
allowedHeaders | array | ['Content-Type', 'Authorization'] | 允许的请求头 |
credentials | bool | false | 是否允许携带凭证(Cookies) |
启动服务器
使用提供的 server.php 启动服务器:
# 启动服务器(前台运行)
php server.php
# 以守护进程方式启动(后台运行)
php server.php -d
# 查看服务器状态
php server.php status
# 停止服务器
php server.php stop
更多信息
详细使用说明请参考 docs/USAGE.md 文件,包括:
- 完整的三层中间件系统文档
- 命名空间正确用法
- ACK 机制详细说明
- API 参考文档
- 故障排除指南
- 性能优化建议
- 安全注意事项
版本历史
v1.6.0:完整架构优化重构,实现高内聚低耦合,多项性能优化,PHPStan 0错误
- 新增SessionStore专门管理会话存储
- 新增SocketEventEmitter Trait,降低Socket类复杂度
- 统一ACK回调管理
- 优化Set类查找性能O(n) → O(1)
- PacketParser使用静态映射替代枚举遍历
- Broadcaster链式调用优化使用clone
- 异常类合并为1个统一类
- EngineIOHandler使用回调解耦
- 消除双重分派和冗余代码
- Adapter新增跨进程方法:allSockets、fetchSockets、socketsJoin、socketsLeave、disconnectSockets
- 大量性能优化点
v1.5.0:完整实现 Socket.IO v4 连接恢复机制,添加
$socket->recovered属性,支持状态恢复、房间恢复和丢失消息重发- v1.4.0:PHP 8.1+优化,完整的三层中间件系统,修复ACK机制,统一$io->of()用法
- v1.3.0:优化性能和稳定性,添加PSR-3日志
- v1.2.0:添加集群模式支持
- v1.1.0:添加二进制数据传输支持
- v1.0.0:初始版本,支持Socket.IO v4协议
贡献指南
欢迎提交 Issue 和 Pull Request 来改进这个项目。在提交代码前,请确保:
- 代码符合项目的代码风格(遵循 PSR 规范)
- 添加了适当的测试
- 文档已经更新(README.md, USAGE.md 等)
- 所有 PHP 语法检查通过
许可证
本项目采用 MulanPSL-2.0 许可证,详见 LICENSE 文件。