本文目录导读:

PHP 双工模式(双向通信)通常指在单个连接上实现客户端和服务器之间的实时、双向数据交换,在 PHP 生态中,常见的实现方式有 WebSocket、SSE、Long Polling 和 PHP 与 Node.js/Go 混合 模式。
这里重点介绍 基于 WebSocket 的双工模式设计,因为这是 PHP 中最正宗、最常用的双工通信方案。
核心设计思路
双工模式 = 一个长连接 + 双方都能主动推送数据。
PHP 传统是“请求-响应”模式(短连接),要实现双工,必须使用事件驱动或非阻塞模型,并配合 WebSocket 协议。
三种主流实现方案
使用 Swoole 或 Workerman(推荐)
这是 PHP 实现双工最成熟、性能最高的方式,Swoole/Workerman 是常驻内存的 PHP 扩展或框架,原生支持 WebSocket 协议。
设计示例(Workerman):
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
// 1. 创建 WebSocket 服务器(监听 8080 端口)
$ws_worker = new Worker('websocket://0.0.0.0:8080');
// 2. 定义连接数量(控制并发)
$ws_worker->count = 4; // 4个进程
// 3. 当客户端连接时触发
$ws_worker->onConnect = function(TcpConnection $connection) {
echo "新连接: {$connection->id}\n";
};
// 4. 当收到客户端消息时触发(服务端可以回复,实现双工)
$ws_worker->onMessage = function(TcpConnection $connection, $data) {
echo "收到: {$data}\n";
// 双工:服务器可以主动推送消息(不仅限于回复)
$connection->send('服务器主动推送:时间戳 ' . time());
// 也可以广播给所有连接
foreach ($connection->worker->connections as $conn) {
$conn->send("用户 {$connection->id} 说: {$data}");
}
};
// 5. 当连接关闭时触发
$ws_worker->onClose = function(TcpConnection $connection) {
echo "连接关闭: {$connection->id}\n";
};
Worker::runAll();
运行方法:
php server.php start
方案优势:
- 真正的全双工(双方随时发消息)
- 支持高并发(异步非阻塞)
- 自带心跳检测、断线重连处理
使用 ReactPHP + Ratchet(纯 PHP 协程)
如果不想安装 Swoole/Workerman 扩展,可以使用纯 PHP 的协程库 ReactPHP 配合 Ratchet。
// composer require cboden/ratchet
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
class Chat implements MessageComponentInterface {
protected $clients;
public function __construct() {
$this->clients = new \SplObjectStorage;
}
public function onOpen(ConnectionInterface $conn) {
$this->clients->attach($conn);
echo "新连接: {$conn->resourceId}\n";
}
public function onMessage(ConnectionInterface $from, $msg) {
// 双工逻辑:服务器可以广播消息
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send("用户 {$from->resourceId} 说: {$msg}");
}
}
}
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "连接关闭\n";
}
public function onError(ConnectionInterface $conn, \Exception $e) {
$conn->close();
}
}
// 启动服务器
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
$server->run();
通过 PHP-FPM + Redis 发布订阅 + 轮询(伪双工)
如果无法使用长驻进程,可以模拟双工:
- 客户端:通过 WebSocket 连接到一个轻量级服务(如 Node.js)
- PHP 后端:接收到 HTTP 请求后,写入 Redis 队列
- Node.js 服务:订阅 Redis,收到消息后通过 WebSocket 推送给客户端
[浏览器] <--WebSocket--> [Node.js 网关] <--Redis--> [PHP 后端]
这种方式 PHP 依然是“请求-响应”,但整体系统实现了双工。
双工模式设计的关键要点
| 要点 | 说明 |
|---|---|
| 心跳机制 | 每 30-60 秒发送 ping/pong,检测死连接 |
| 消息编码 | 使用 JSON 或 Protocol Buffers,包含 type、data、from 等字段 |
| 身份认证 | 连接建立时通过 Token 验证,避免未授权访问 |
| 消息确认 | 对于重要消息,客户端需返回 ACK,服务端未收到则重发 |
| 断线重连 | 客户端实现自动重连,服务端记录会话状态 |
| 并发控制 | 使用 Swoole/Workerman 的进程模型,避免阻塞 |
示例:WebSocket 双工消息格式
// 客户端 -> 服务端
{
"type": "message",
"payload": {
"to": "user_123",
"content": "你好",
"timestamp": 1715000000
},
"token": "xxx"
}
// 服务端 -> 客户端
{
"type": "broadcast",
"payload": {
"from": "user_456",
"content": "大家好",
"timestamp": 1715000100
}
}
// 服务端 -> 单个客户端(认证成功)
{
"type": "auth_success",
"payload": {
"user_id": "user_123",
"online_count": 42
}
}
// 心跳
{
"type": "ping"
}
// 回复
{
"type": "pong"
}
选择建议
| 场景 | 推荐方案 |
|---|---|
| 高并发实时聊天/游戏 | Swoole(性能最好) |
| 中等规模即时通讯 | Workerman(易用性好) |
| 不想装扩展,纯 PHP | ReactPHP + Ratchet |
| 已有 Node.js 基础设施 | Node.js 网关 + PHP 后端(混合) |
| 简单单向推送(服务器到客户端) | SSE(简单,但非双工) |
PHP 实现双工模式的本质是:
- 放弃短连接,使用长连接(WebSocket / SSE)
- 让 PHP 常驻内存(Swoole/Workerman)或借助中间件(Redis + Node.js)
- 在 onMessage 回调中实现“客户端发消息 → 服务端处理并主动推送”
推荐从 Workerman 开始,因为它:
- 文档丰富,中文友好
- 安装简单(
composer require workerman/workerman) - 性能足够 90% 的 PHP 双工场景
如果你需要具体的代码示例或调试建议,可以告诉我你的具体场景(如聊天、游戏、协作编辑等),我可以提供更针对性的设计。