在 Web 应用中,WebSocket 提供了一种在客户端和服务器之间建立全双工通信通道的方式。相比传统的 HTTP 请求,WebSocket 允许服务器主动向客户端推送消息。虽然 PHP 不是传统意义上最适合处理长连接的语言,但它依然可以通过底层的 socket 编程来实现简单的 WebSocket 服务。本篇文章将介绍如何使用 PHP 的 socket_accept 函数,实现与前端 WebSocket 客户端的通信。
为了使用 PHP 的 socket 功能,需要确保 PHP 已经启用了 sockets 扩展。在 php.ini 中可以启用它:
extension=sockets
然后重启你的 PHP 服务。
WebSocket 的握手过程遵循一定的协议规范,我们需要在接收到客户端请求后,进行一次 HTTP 协议级别的握手确认,之后才能进行 WebSocket 数据帧通信。
以下是一个完整的 PHP 脚本示例:
<?php
$host = '0.0.0.0';
$port = 8080;
$serverSocket = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);
socket_bind($serverSocket, $host, $port);
socket_listen($serverSocket);
echo "WebSocket 服务器启动于 $host:$port\n";
while (true) {
$clientSocket = socket_accept($serverSocket);
if ($clientSocket === false) {
continue;
}
// 读取客户端请求头
$request = socket_read($clientSocket, 1024);
if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $request, $matches)) {
$key = trim($matches[1]);
$acceptKey = base64_encode(pack('H*', sha1($key . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11')));
$headers = "HTTP/1.1 101 Switching Protocols\r\n";
$headers .= "Upgrade: websocket\r\n";
$headers .= "Connection: Upgrade\r\n";
$headers .= "Sec-WebSocket-Accept: $acceptKey\r\n\r\n";
socket_write($clientSocket, $headers);
echo "WebSocket 握手完成。\n";
}
// 简单读取和发送 WebSocket 消息
while (true) {
$data = socket_read($clientSocket, 2048);
if ($data === false) {
break;
}
$decoded = unmask($data);
echo "接收到客户端消息: $decoded\n";
$response = mask("你发送的是: $decoded");
socket_write($clientSocket, $response);
}
socket_close($clientSocket);
}
socket_close($serverSocket);
// 解析 WebSocket 帧
function unmask($payload) {
$length = ord($payload[1]) & 127;
if ($length == 126) {
$masks = substr($payload, 4, 4);
$data = substr($payload, 8);
} elseif ($length == 127) {
$masks = substr($payload, 10, 4);
$data = substr($payload, 14);
} else {
$masks = substr($payload, 2, 4);
$data = substr($payload, 6);
}
$text = '';
for ($i = 0; $i < strlen($data); ++$i) {
$text .= $data[$i] ^ $masks[$i % 4];
}
return $text;
}
// 构造 WebSocket 数据帧
function mask($text) {
$b1 = 0x81; // FIN=1, opcode=0x1 (text)
$length = strlen($text);
if ($length <= 125) {
$header = pack('CC', $b1, $length);
} elseif ($length <= 65535) {
$header = pack('CCn', $b1, 126, $length);
} else {
$header = pack('CCNN', $b1, 127, 0, $length);
}
return $header . $text;
}
?>
在前端,你可以用如下方式与 PHP WebSocket 服务通信:
<script>
const socket = new WebSocket("ws://m66.net:8080");
socket.onopen = () => {
console.log("连接已建立");
socket.send("你好 PHP WebSocket!");
};
socket.onmessage = (event) => {
console.log("收到消息:", event.data);
};
socket.onclose = () => {
console.log("连接已关闭");
};
</script>
注意:这里的 ws://m66.net:8080 是你服务器的地址,请根据部署环境进行域名和端口映射的设置。
通过使用 PHP 的 socket_accept 和相关函数,我们可以实现一个基本的 WebSocket 服务端逻辑。这种方式适合学习和实验用途,对于生产环境而言,建议使用更高性能的解决方案如 Ratchet、Swoole 或使用专门的 WebSocket 服务器(如 Node.js、Go)。
尽管 PHP 并非为长连接设计,但其底层 socket 功能强大,能够满足基本的双向通信需求。通过本文的示例,你可以搭建一个简单的 PHP WebSocket 服务端,与前端客户端进行消息交互。