本文目录导读:

在 PHP 中实现 WebSocket 服务有几种方式,下面我来详细介绍。
使用 Workerman(推荐)
Workerman 是最流行的 PHP WebSocket 解决方案。
安装
composer require workerman/workerman
基本示例
<?php
use Workerman\Worker;
use Workerman\Connection\TcpConnection;
require_once __DIR__ . '/vendor/autoload.php';
// 创建 WebSocket 服务,监听 2346 端口
$ws_worker = new Worker("websocket://0.0.0.0:2346");
// 设置进程数
$ws_worker->count = 4;
// 客户端连接时触发
$ws_worker->onConnect = function(TcpConnection $connection) {
echo "新连接建立\n";
};
// 收到客户端消息时触发
$ws_worker->onMessage = function(TcpConnection $connection, $data) {
echo "收到消息: $data\n";
// 发送消息给客户端
$connection->send("服务器收到: $data");
// 广播给所有客户端
foreach ($connection->worker->connections as $client) {
$client->send("广播消息: $data");
}
};
// 客户端断开时触发
$ws_worker->onClose = function(TcpConnection $connection) {
echo "连接关闭\n";
};
// 启动服务
Worker::runAll();
运行方式
# 启动服务(前台运行) php websocket.php start # 后台运行 php websocket.php start -d # 停止服务 php websocket.php stop
使用 Ratchet
Ratchet 是另一个常用的 WebSocket 库。
安装
composer require cboden/ratchet
基本示例
<?php
use Ratchet\MessageComponentInterface;
use Ratchet\ConnectionInterface;
use Ratchet\Http\HttpServer;
use Ratchet\Server\IoServer;
use Ratchet\WebSocket\WsServer;
require_once __DIR__ . '/vendor/autoload.php';
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) {
echo "收到消息: $msg\n";
// 广播给所有客户端
foreach ($this->clients as $client) {
if ($from !== $client) {
$client->send($msg);
}
}
}
// 关闭连接
public function onClose(ConnectionInterface $conn) {
$this->clients->detach($conn);
echo "连接关闭\n";
}
// 错误处理
public function onError(ConnectionInterface $conn, \Exception $e) {
echo "错误: {$e->getMessage()}\n";
$conn->close();
}
}
// 创建服务
$server = IoServer::factory(
new HttpServer(
new WsServer(
new Chat()
)
),
8080
);
echo "WebSocket 服务器运行在端口 8080\n";
$server->run();
运行方式
php chat-server.php
原生 PHP WebSocket 实现
如果不想使用第三方库,也可以原生实现(比较复杂):
<?php
class WebSocketServer {
private $clients = [];
private $socket;
public function __construct($host, $port) {
$this->socket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_set_option($this->socket, SOL_SOCKET, SO_REUSEADDR, 1);
socket_bind($this->socket, $host, $port);
socket_listen($this->socket);
echo "WebSocket 服务器运行在 $host:$port\n";
}
public function run() {
while (true) {
$read = $this->clients;
$read[] = $this->socket;
socket_select($read, $write, $except, null);
// 新连接
if (in_array($this->socket, $read)) {
$newClient = socket_accept($this->socket);
$this->handshake($newClient);
$this->clients[] = $newClient;
}
// 处理客户端消息
foreach ($read as $client) {
if ($client === $this->socket) continue;
$data = @socket_read($client, 1024);
if ($data) {
$data = $this->decode($data);
echo "收到: $data\n";
// 广播给所有客户端
foreach ($this->clients as $c) {
if ($c !== $client) {
socket_write($c, $this->encode($data));
}
}
} else {
// 客户端断开
$index = array_search($client, $this->clients);
unset($this->clients[$index]);
socket_close($client);
}
}
}
}
// WebSocket 握手
private function handshake($client) {
$request = socket_read($client, 1024);
preg_match('/Sec-WebSocket-Key:\s(.*)\r\n/', $request, $matches);
$key = $matches[1] ?? '';
$accept = base64_encode(sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));
$response = "HTTP/1.1 101 Switching Protocols\r\n"
. "Upgrade: websocket\r\n"
. "Connection: Upgrade\r\n"
. "Sec-WebSocket-Accept: $accept\r\n\r\n";
socket_write($client, $response);
}
// 解码客户端消息
private function decode($payload) {
$length = ord($payload[1]) & 127;
$mask = substr($payload, 2, 4);
$data = substr($payload, 6);
$decoded = '';
for ($i = 0; $i < strlen($data); $i++) {
$decoded .= $data[$i] ^ $mask[$i % 4];
}
return $decoded;
}
// 编码发送给客户端的消息
private function encode($data) {
$payload = '81';
$len = strlen($data);
if ($len <= 125) {
$payload .= sprintf('%02X', $len);
} elseif ($len <= 65535) {
$payload .= '7E' . sprintf('%04X', $len);
} else {
$payload .= '7F' . sprintf('%08X', $len);
}
$payload .= bin2hex($data);
return hex2bin($payload);
}
}
// 使用
$server = new WebSocketServer('0.0.0.0', 8080);
$server->run();
客户端示例(JavaScript)
// 连接 WebSocket
const ws = new WebSocket('ws://localhost:2346');
// 连接建立时
ws.onopen = function() {
console.log('连接成功');
ws.send('Hello Server!');
};
// 接收消息
ws.onmessage = function(event) {
console.log('收到服务器消息:', event.data);
};
// 关闭连接
ws.onclose = function() {
console.log('连接关闭');
};
// 错误处理
ws.onerror = function(error) {
console.error('错误:', error);
};
推荐方案对比
| 方案 | 优点 | 缺点 | 适用场景 |
|---|---|---|---|
| Workerman | 性能好、功能全、文档丰富 | 需要 PHP CLI 环境 | 生产环境、高性能需求 |
| Ratchet | 简单易用、纯 PHP | 性能一般 | 中小型项目 |
| 原生实现 | 无依赖 | 复杂、容易出错 | 学习研究 |
部署注意事项
- 使用 PHP CLI 模式:WebSocket 服务需要长驻内存,不能使用 PHP-FPM
- 防火墙配置:确保 WebSocket 端口对外开放
- 反向代理:可以使用 Nginx 反向代理 WebSocket
- SSL 支持:生产环境建议使用 WSS(WebSocket Secure)
如果需要更多帮助,请告诉我你的具体使用场景!