php不直接支持websocket服务器,需借助swoole等扩展实现;纯php socket实现复杂、性能差、难维护,而swoole提供异步、协程websocket服务,api简洁且生产就绪。

PHP 本身不直接支持 WebSocket 服务器(它不是像 Node.js 那样原生事件驱动的运行时),但可以通过 PHP CLI 启动一个常驻进程,结合 socket 扩展手动实现 WebSocket 握手与通信逻辑。不过更推荐、更稳定、更常用的方式是:用 PHP 写业务逻辑(如用户管理、消息存储),用专门的 WebSocket 服务(如 Swoole、Workerman)承载连接和实时通信。
为什么不用纯 PHP socket 写 WebSocket?
WebSocket 协议包含复杂的手动帧解析(掩码、opcode、长度编码、FIN 标志等),纯 PHP 实现易出错、性能差、难以处理高并发和心跳保活。官方 socket 扩展缺乏异步 I/O 支持,无法高效管理成百上千长连接。
- 握手需正确响应 Sec-WebSocket-Accept 头(基于 base64(sha1(key + "258EAFA5-E914-47DA-95CA-C5AB0DC85B11")))
- 客户端发来的数据帧必须解掩码(mask key 每帧不同)
- 服务端发给客户端的数据帧必须加掩码(规范强制要求)
- 需自行实现 ping/pong 心跳、连接超时、异常断连清理
推荐方案:Swoole + PHP(轻量、高性能、生产就绪)
Swoole 是基于 C 的 PHP 扩展,提供异步、多线程、协程 WebSocket 服务器能力,API 简洁,可直接在 PHP 中启动 WebSocket 服务,并无缝调用其他 PHP 类库(如 PDO、Redis)。
前提:已安装 Swoole(≥ v4.8.0,推荐 v5.x),PHP ≥ 7.4,启用 extension=swoole.so
完整可运行聊天室代码(含服务端 + 前端页面):
1. WebSocket 服务端(server.php)
保存为 server.php,终端执行 php server.php
<?php use Swoole\WebSocket\Server;
use Swoole\Http\Request;
use Swoole\WebSocket\Frame;
$server = new Server('0.0.0.0', 9501);
// 连接建立
$server->on('open', function (Server $server, Request $request) {
echo "Client {$request->fd} connected.\n";
});
// 收到消息(文本或二进制)
$server->on('message', function (Server $server, Frame $frame) {
$data = $frame->data;
$fd = $frame->fd;
// 简单 JSON 格式:{"type":"msg","nick":"张三","content":"hello"}
$msg = json_decode($data, true);
if (!$msg || !isset($msg['type'])) return;
if ($msg['type'] === 'msg' && !empty($msg['nick']) && !empty($msg['content'])) {
$broadcast = json_encode([
'type' => 'broadcast',
'from' => $msg['nick'],
'content' => htmlspecialchars($msg['content']),
'time' => date('H:i:s')
]);
// 广播给所有在线客户端(不含自己)
foreach ($server->connections as $clientFd) {
if ($clientFd != $fd && $server->isEstablished($clientFd)) {
$server->push($clientFd, $broadcast);
}
}
}
});
// 连接关闭
$server->on('close', function (Server $server, int $fd) {
echo "Client {$fd} disconnected.\n";
});
echo "WebSocket server started at ws://127.0.0.1:9501\n";
$server->start();
2. 前端网页(index.html)
放在 Web 服务器(如 Nginx/Apache)下访问,例如 http://localhost/index.html
<title>PHP 聊天室(Swoole WebSocket)</title><style>
body { font-family: sans-serif; max-width: 600px; margin: 20px auto; padding: 0 10px; }
#chat { height: 400px; border: 1px solid #ccc; overflow-y: scroll; padding: 10px; }
.msg { margin: 5px 0; }
.nick { font-weight: bold; color: #0066cc; }
.time { font-size: 0.8em; color: #999; margin-left: 8px; }
.content { word-break: break-word; }
#input-area { margin-top: 10px; }
input, button { padding: 8px; font-size: 14px; }
#nick { width: 120px; }
#msg { width: 300px; }
</style><h2>? 实时聊天室</h2>
<p>昵称:<input type="text" id="nick" value="游客" placeholder="输入昵称"></p>
<div id="chat"></div>
<div id="input-area">
<input type="text" id="msg" placeholder="按 Enter 发送"><button onclick="send()">发送</button>
</div>
<script>
let ws;
const nickInput = document.getElementById('nick');
const msgInput = document.getElementById('msg');
const chatBox = document.getElementById('chat');
function connect() {
const wsUrl = 'ws://' + window.location.host + ':9501';
ws = new WebSocket(wsUrl);
ws.onopen = () => {
appendMsg('✅ 已连接服务器', 'system');
};
ws.onmessage = (event) => {
const data = JSON.parse(event.data);
if (data.type === 'broadcast') {
appendMsg(`【${data.from}】${data.content}`, 'user', data.time);
}
};
ws.onclose = () => {
appendMsg('❌ 连接已断开,正在重连...', 'system');
setTimeout(connect, 3000);
};
ws.onerror = (err) => {
console.error('WebSocket error:', err);
};
}
function send() {
const nick = nickInput.value.trim() || '匿名';
const content = msgInput.value.trim();
if (!content) return;
const payload = JSON.stringify({
type: 'msg',
nick: nick,
content: content
});
if (ws && ws.readyState === WebSocket.OPEN) {
ws.send(payload);
msgInput.value = '';
}
}
function appendMsg(text, type, time = null) {
const div = document.createElement('div');
div.className = 'msg';
if (type === 'user') {
div.innerHTML = `<span class="nick">${text.split('】')[0].slice(1)}` +
`<span class="time">${time || ''}` +
`<div class="content">${text.split('】')[1] || text}`;
} else {
div.innerHTML = `<span class="system">${text}`;
}
chatBox.appendChild(div);
chatBox.scrollTop = chatBox.scrollHeight;
}
// 回车发送
msgInput.addEventListener('keypress', (e) => {
if (e.key === 'Enter') send();
});
// 自动连接
connect();
</script>
3. 运行步骤
- 确保 Swoole 已启用:
php -m | grep swoole - 启动服务端:
php server.php(保持终端运行) - 将
index.html放入 Web 目录(如/var/www/html/),用浏览器打开http://localhost/index.html - 开多个浏览器标签页测试多人聊天
增强建议(上线前必加)
- 身份唯一性:用 session_id 或 JWT 绑定 fd,避免昵称伪造
- 消息持久化:收到消息后写入 MySQL 或 Redis,供历史查看
- 房间隔离:在 message 中加 room 字段,广播时过滤目标房间
- 防刷机制:限制单个 fd 每秒最大消息数(Swoole 提供 rate limiter)
-
HTTPS + WSS:Nginx 反向代理 WebSocket,开启 SSL,前端改用
wss://
不复杂但容易忽略:WebSocket 是长连接,务必在反代配置中设置 proxy_read_timeout 60; 和 Upgrade $http_upgrade; 头,否则 Nginx 会主动断连。
php免费学习视频:立即使用
踏上前端学习之旅,开启通往精通之路!从前端基础到项目实战,循序渐进,一步一个脚印,迈向巅峰!











