当前位置: 首页 > news >正文

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开启服务监听,然后再把浏览器打开,就可以发送信息且接收到信息了。

http://www.lryc.cn/news/578143.html

相关文章:

  • 从入门到精通:npm、npx、nvm 包管理工具详解及常用命令
  • Springboot + vue + uni-app小程序web端全套家具商场
  • 【Spring】——事务、整合、注解
  • 设计模式-观察者模式(发布订阅模式)
  • UE5 - 制作《塞尔达传说》中林克的技能 - 17 - 遥控炸弹(二)
  • 键盘第一下无反应
  • 基于Spring Boot的绿园社区团购系统的设计与实现
  • 磁悬浮轴承位移信号的高精度估计:卡尔曼滤波算法深度解析
  • MySQL复杂SQL性能优化实战:多表联查与子查询的高效方法
  • 【数据清洗与预处理】-文本采集与处理
  • LoRA 问答微调与部署全流程:基于 LLaMA-Factory + DeepSeek + FastAPI 打造专属大模型
  • Hive SQL 实战:电商销售数据分析全流程案例
  • 大数据轻量化流批一体架构探索实践(一)
  • 【数据分析】环境数据降维与聚类分析教程:从PCA到可视化
  • [特殊字符]【联邦学习实战】用 PyTorch 从 0 搭建一个最简单的联邦学习系统(含完整代码)
  • ubuntu下免sudo执行docker
  • spring-ai-alibaba官方 Playground 示例
  • 根据OS自动加载不同的native库和本地jar包
  • Ollama 深度使用指南:在本地玩转大型语言模型
  • 关于Spring的那点事(1)
  • AIGC检测系统升级后的AI内容识别机制与系统性降重策略研究(三阶段降重法)
  • 04_MySQL 通过 Docker 在同一个服务器上搭建主从集群(一主一从)
  • Junit_注解_枚举
  • 【区块链安全】代理合约中的漏洞
  • 【C++指南】C++ list容器完全解读(三):list迭代器的实现与优化
  • 【软考高项论文】论信息系统项目的成本管理
  • 渗透测试的重要性及最佳实践
  • 对selenium进行浏览器和驱动进行配置Windows | Linux
  • 调试W5500(作为服务器)
  • 淘宝API接口在数据分析中的应用