在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 服務端,與前端客戶端進行消息交互。