PHP WebSocket服务器搭建指南
该代码实现了一个原生的WebSocket服务器,与客户端保持长连接,接受信息后,广播通知其他链接的客户端。
以下是服务器端代码:
<?php
class WebSocketServer
{private $sockets = [];private $master;public function __construct($host = '0.0.0.0', $port = 8000){$this->master = socket_create(AF_INET, SOCK_STREAM, SOL_TCP);socket_set_option($this->master, SOL_SOCKET, SO_REUSEADDR, 1);socket_bind($this->master, $host, $port);socket_listen($this->master);$this->sockets[] = $this->master;echo "Server started on ws://{$host}:{$port}\n";}private function handshake($buffer, $socket){if (preg_match("/Sec-WebSocket-Key: (.*)\r\n/", $buffer, $match)){$key = base64_encode(sha1($match[1] . '258EAFA5-E914-47DA-95CA-C5AB0DC85B11', true));$headers = "HTTP/1.1 101 Switching Protocols\r\n";$headers .= "Upgrade: websocket\r\n";$headers .= "Connection: Upgrade\r\n";$headers .= "Sec-WebSocket-Accept: $key\r\n\r\n";socket_write($socket, $headers, strlen($headers));return true;}return false;}private 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;}private function mask($text) {$b1 = 0x80 | (0x1 & 0x0f);$length = strlen($text);if ($length <= 125) {$header = pack('CC', $b1, $length);} elseif ($length > 125 && $length < 65536) {$header = pack('CCn', $b1, 126, $length);} else {$header = pack('CCNN', $b1, 127, $length);}return $header . $text;}public function run() {while (true){$changed = $this->sockets;$write = $except = null; // 显式赋 nullsocket_select($changed, $write, $except, null);foreach ($changed as $socket){//活跃socket是新生成的对象if ($socket == $this->master){$client = socket_accept($this->master);$this->sockets[] = $client;}else{$bytes = @socket_recv($socket, $buffer, 2048, 0);//客户端关闭if ($bytes === 0){$index = array_search($socket, $this->sockets);unset($this->sockets[$index]);socket_close($socket);}else{//websocket握手升级协议开始建立长连接if (stripos($buffer,'Sec-WebSocket-Key')){$this->handshake($buffer, $socket);}else{$message = $this->unmask($buffer);echo "Received: {$message}\n";$response = $this->mask("Server received: {$message}");//通知所有链接的客户端foreach($this->sockets as $socketItem){//排除总机监听的socket类if($this->master != $socketItem){socket_write($socketItem, $response, strlen($response));}}}}}}}}
}$server = new WebSocketServer();
$server->run();
客户端代码:
<!DOCTYPE html>
<html>
<head><title>Socket客户端</title><style>#console { height:300px; border:1px solid #ccc; overflow-y:scroll; padding:10px; }.status { color:#666; margin:10px 0; }</style>
</head>
<body><div class="status" id="status">准备连接服务器...</div><div id="console"></div><input type="text" id="message" placeholder="输入消息"><button onclick="send()">发送</button><script>const socket = new WebSocket('ws://localhost:8000');const statusEl = document.getElementById('status');socket.onopen = () => {statusEl.textContent = "已连接到服务器";log('系统: 连接成功');};socket.onmessage = (e) => {log('服务器: ' + e.data);};socket.onerror = (e) => {statusEl.textContent = "连接错误";log('系统: 错误: ' + e.message);};socket.onclose = () => {statusEl.textContent = "连接已关闭";log('系统: 连接断开');};function send() {const msg = document.getElementById('message').value;if(socket.readyState === WebSocket.OPEN) {socket.send(msg);log('我: ' + msg);document.getElementById('message').value = '';}}function log(msg) {const console = document.getElementById('console');const p = document.createElement('p');p.textContent = msg;console.appendChild(p);console.scrollTop = console.scrollHeight;}</script>
</body>
</html>
这样两部分就都开发完毕了。
然后我们用命令行启动php开启服务监听,然后再把浏览器打开,就可以发送信息且接收到信息了。