ThinkPHP6.1+Ratchet库 搭建websocket服务

Ratchet 是一个基于 ReactPHP 的 PHP WebSocket 库,无需依赖 Swoole 扩展。以下是实现步骤:

首先安装 Ratchet:

composer require cboden/ratchet

创建 WebSocket 处理类:

<?php
/*** websocket处理类* @DateTime  2025/7/28 10:38*/namespace app\api\controller;use Ratchet\ConnectionInterface;
use Ratchet\MessageComponentInterface;class WebSocketSever implements MessageComponentInterface
{protected $clients;public function __construct(){$this->clients = new \SplObjectStorage;echo "Chat server initialized\n";}// 实现接口要求的四个方法public function onOpen(ConnectionInterface $conn){$this->clients->attach($conn);$clientId = $conn->resourceId;echo "新客户端连接: ({$clientId})\n";$conn->send("连接成功!你的客户端ID是: {$clientId}");}public function onMessage(ConnectionInterface $from, $msg){$clientId = $from->resourceId;echo "客户端 {$clientId} 发送消息: {$msg}\n";// 向发送者确认$from->send("服务器已收到: {$msg}");// 广播给其他客户端foreach ($this->clients as $client) {if ($from !== $client) {$client->send("用户 {$clientId} 说: {$msg}");}}}public function onClose(ConnectionInterface $conn){$this->clients->detach($conn);echo "客户端 {$conn->resourceId} 断开连接\n";}public function onError(ConnectionInterface $conn, \Exception $e){echo "发生错误: {$e->getMessage()}\n";$conn->close();}
}

创建 WebSocket 服务启动文件start_server.php:

注意:这里不能使用控制器,不要问为什么,问就是我已经尝试过了,当然你也可以尝试一下。

<?php
/*** websocket启动文件 start_server.php* @DateTime  2025/7/28 16:14*/
require 'vendor/autoload.php';use Ratchet\Server\IoServer;
use Ratchet\Http\HttpServer;
use Ratchet\WebSocket\WsServer;
use app\api\controller\WebSocketSever;$server =IoServer::factory(new HttpServer(new WsServer(new WebSocketSever())),9501 // 端口号
);echo "Ratchet WebSocket服务器已启动: ws://127.0.0.1:9501\n";
echo "按 Ctrl+C 停止服务\n";
$server->run();

 前端测试页面:

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>WebSocket 测试工具</title><script src="https://cdn.tailwindcss.com"></script><link href="https://cdn.jsdelivr.net/npm/font-awesome@4.7.0/css/font-awesome.min.css" rel="stylesheet"><!-- 配置Tailwind自定义颜色和动画 --><script>tailwind.config = {theme: {extend: {colors: {primary: '#3B82F6',secondary: '#10B981',danger: '#EF4444',dark: '#1E293B',light: '#F8FAFC'},fontFamily: {sans: ['Inter', 'system-ui', 'sans-serif'],},animation: {'fade-in': 'fadeIn 0.3s ease-in-out',},keyframes: {fadeIn: {'0%': { opacity: '0' },'100%': { opacity: '1' },}}}}}</script><style type="text/tailwindcss">@layer utilities {.content-auto {content-visibility: auto;}.scrollbar-hide {scrollbar-width: none;-ms-overflow-style: none;}.scrollbar-hide::-webkit-scrollbar {display: none;}.message-box {@apply rounded-lg p-4 mb-3 max-w-[80%] animate-fade-in;}.sent-message {@apply bg-primary text-white ml-auto;}.received-message {@apply bg-gray-200 text-dark mr-auto;}.system-message {@apply bg-gray-100 text-gray-500 text-sm mx-auto;}}</style>
</head>
<body class="bg-gray-50 min-h-screen font-sans">
<div class="container mx-auto px-4 py-8 max-w-4xl"><header class="mb-8 text-center"><h1 class="text-[clamp(1.8rem,4vw,2.5rem)] font-bold text-dark mb-2"><i class="fa fa-comments text-primary mr-2"></i>WebSocket 测试工具</h1><p class="text-gray-600">与 Ratchet WebSocket 服务器的实时通信测试</p></header><!-- 连接状态区域 --><div class="bg-white rounded-xl shadow-md p-4 mb-6 transition-all duration-300"><div class="flex items-center justify-between"><div class="flex items-center"><div id="connection-status" class="w-3 h-3 rounded-full bg-danger mr-2"></div><span id="status-text" class="text-danger font-medium">未连接</span></div><div class="flex space-x-2"><input type="text" id="server-url"value="ws://127.0.0.1:9501"class="px-3 py-2 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 text-sm w-64"><button id="connect-btn" class="bg-primary hover:bg-primary/90 text-white px-4 py-2 rounded-lg transition-all duration-200 flex items-center"><i class="fa fa-plug mr-1"></i> 连接</button></div></div><div class="mt-3 text-sm text-gray-500 flex items-center"><i class="fa fa-info-circle mr-1"></i><span>服务器状态: <span id="server-info">等待连接...</span></span></div></div><!-- 消息显示区域 --><div class="bg-white rounded-xl shadow-md p-4 mb-6 h-[500px] flex flex-col"><div class="text-sm font-medium text-gray-500 mb-3 border-b pb-2"><i class="fa fa-history mr-1"></i> 消息历史</div><div id="messages" class="flex-1 overflow-y-auto scrollbar-hide p-2 space-y-2"><!-- 消息会动态添加到这里 --><div class="system-message message-box"><i class="fa fa-info-circle mr-1"></i>请点击"连接"按钮与服务器建立连接</div></div></div><!-- 消息输入区域 --><div class="bg-white rounded-xl shadow-md p-4"><div class="flex space-x-3"><textareaid="messageInput"placeholder="输入消息内容...(按Enter发送,Shift+Enter换行)"class="flex-1 px-4 py-3 border border-gray-300 rounded-lg focus:outline-none focus:ring-2 focus:ring-primary/50 resize-none"rows="3"></textarea><buttonid="send-btn"class="bg-secondary hover:bg-secondary/90 text-white px-6 py-3 rounded-lg transition-all duration-200 flex items-center justify-center disabled:opacity-50 disabled:cursor-not-allowed"disabled><i class="fa fa-paper-plane mr-1"></i> 发送</button></div><div class="mt-3 text-xs text-gray-400 flex justify-between"><div><i class="fa fa-keyboard-o mr-1"></i> 快捷键: Enter发送消息</div><div id="client-id" class="hidden"><i class="fa fa-user mr-1"></i> 客户端ID: <span></span></div></div></div><footer class="mt-8 text-center text-gray-500 text-sm"><p>WebSocket 测试工具 &copy; 2023</p></footer>
</div><script>// DOM元素const connectBtn = document.getElementById('connect-btn');const sendBtn = document.getElementById('send-btn');const messageInput = document.getElementById('messageInput');const messagesDiv = document.getElementById('messages');const connectionStatus = document.getElementById('connection-status');const statusText = document.getElementById('status-text');const serverInfo = document.getElementById('server-info');const serverUrlInput = document.getElementById('server-url');const clientIdElement = document.getElementById('client-id').querySelector('span');const clientIdContainer = document.getElementById('client-id');// WebSocket实例let ws = null;let isConnected = false;// 连接状态样式更新function updateConnectionStatus(connected) {isConnected = connected;if (connected) {connectionStatus.classList.remove('bg-danger');connectionStatus.classList.add('bg-secondary');statusText.classList.remove('text-danger');statusText.classList.add('text-secondary');statusText.textContent = '已连接';connectBtn.innerHTML = '<i class="fa fa-unplug mr-1"></i> 断开';connectBtn.classList.remove('bg-primary');connectBtn.classList.add('bg-danger');sendBtn.disabled = false;messageInput.focus();serverInfo.textContent = `已连接到 ${serverUrlInput.value}`;clientIdContainer.classList.remove('hidden');} else {connectionStatus.classList.remove('bg-secondary');connectionStatus.classList.add('bg-danger');statusText.classList.remove('text-secondary');statusText.classList.add('text-danger');statusText.textContent = '未连接';connectBtn.innerHTML = '<i class="fa fa-plug mr-1"></i> 连接';connectBtn.classList.remove('bg-danger');connectBtn.classList.add('bg-primary');sendBtn.disabled = true;serverInfo.textContent = '未连接到服务器';clientIdContainer.classList.add('hidden');}}// 显示消息function showMessage(text, type = 'received') {const messageElement = document.createElement('div');// 设置消息类型样式if (type === 'sent') {messageElement.classList.add('sent-message', 'message-box');} else if (type === 'received') {messageElement.classList.add('received-message', 'message-box');} else if (type === 'system') {messageElement.classList.add('system-message', 'message-box');}// 添加消息内容messageElement.textContent = text;messagesDiv.appendChild(messageElement);// 滚动到底部messagesDiv.scrollTop = messagesDiv.scrollHeight;}// 连接/断开WebSocketfunction toggleConnection() {if (isConnected) {// 断开连接if (ws) {ws.close();}} else {// 建立连接const serverUrl = serverUrlInput.value.trim();if (!serverUrl) {showMessage('请输入服务器地址', 'system');return;}try {ws = new WebSocket(serverUrl);// 连接成功ws.onopen = function() {updateConnectionStatus(true);showMessage('成功连接到服务器', 'system');};// 接收消息ws.onmessage = function(evt) {console.log('收到消息: ' + evt.data);// 尝试提取客户端ID(如果服务器返回)if (evt.data.startsWith('你的客户端ID是:')) {const clientId = evt.data.split(':')[1].trim();clientIdElement.textContent = clientId;}showMessage(evt.data, 'received');};// 连接关闭ws.onclose = function(event) {updateConnectionStatus(false);let message = '连接已关闭';if (event.code !== 1000) {message += ` (错误代码: ${event.code})`;}showMessage(message, 'system');};// 连接错误ws.onerror = function(error) {showMessage(`连接错误: ${error.message || '未知错误'}`, 'system');console.error('WebSocket错误:', error);};} catch (error) {showMessage(`连接失败: ${error.message}`, 'system');console.error('连接异常:', error);}}}// 发送消息function sendMessage() {if (!isConnected || !ws) {showMessage('请先连接到服务器', 'system');return;}const message = messageInput.value.trim();if (!message) return;try {ws.send(message);showMessage(`我: ${message}`, 'sent');messageInput.value = '';} catch (error) {showMessage(`发送失败: ${error.message}`, 'system');console.error('发送消息错误:', error);}}// 事件监听connectBtn.addEventListener('click', toggleConnection);sendBtn.addEventListener('click', sendMessage);// 处理文本框回车事件messageInput.addEventListener('keydown', function(e) {// Enter键且没有按Shiftif (e.key === 'Enter' && !e.shiftKey) {e.preventDefault(); // 阻止默认换行sendMessage();}});// 服务器地址输入框事件serverUrlInput.addEventListener('keydown', function(e) {if (e.key === 'Enter') {e.preventDefault();if (!isConnected) {toggleConnection();}}});
</script>
</body>
</html>

启动服务:

php start_server.php

启动结果:

访问测试页面即可进行 WebSocket 通信。

点击【连接】后的页面:

与 Swoole 相比,Ratchet 的优势是不需要安装 PHP 扩展,纯 PHP 实现,兼容性更好;缺点是在高并发场景下性能可能不如 Swoole。

下一篇文章尝试使用Swoole来搭建websocket服务。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/bicheng/91129.shtml
繁体地址,请注明出处:http://hk.pswp.cn/bicheng/91129.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

智慧工地系统:科技如何重塑建筑现场?

前几天路过一个正在施工的楼盘&#xff0c;看到现场虽然机器轰鸣&#xff0c;但秩序井然&#xff0c;工人们佩戴着设备&#xff0c;指挥塔上闪烁着指示灯&#xff0c;和印象中那种尘土飞扬、杂乱无章的工地景象完全不同。当时就感慨&#xff0c;现在工地也“智慧”起来了。后来…

Day 25:异常处理

Day 25: Python异常处理机制 Review 上一节主要是熟悉os等python中的文件操作&#xff0c;包含&#xff1a; 基础操作&#xff1a;目录获取、文件列举、路径拼接系统交互&#xff1a;环境变量管理、跨平台兼容性高级功能&#xff1a;目录树遍历、文件系统分析 Today 今天专…

Apache Ignite 的分布式队列(IgniteQueue)和分布式集合(IgniteSet)的介绍

以下的内容是关于 Apache Ignite 的分布式队列&#xff08;IgniteQueue&#xff09;和分布式集合&#xff08;IgniteSet&#xff09; 的介绍。它们是 Ignite 提供的分布式数据结构&#xff0c;让你可以在整个集群中像使用本地 BlockingQueue 或 Set 一样操作共享的数据。 下面我…

HTML5 `<figure>` 标签:提升网页语义化与可访问性的利器

目录什么是 <figure> 标签&#xff1f;为什么我们要用 <figure>&#xff1f;<figure> 标签的语法<figure> 标签的适用场景1 图片及其说明 (最常用)2 代码片段及其注释3 图表、流程图或数据可视化4 引用或引文 (Quote) 及其出处总结在现代网页开发中&am…

计算机网络五层模型

我们常说的“计算机网络五层协议模型”&#xff0c;是一个实际应用中广泛采用的简化模型&#xff08;介于OSI七层&#xff08;Open System Interconnect&#xff09;与TCP/IP四层之间&#xff09;&#xff0c;用于描述网络通信中各层的职责与作用。 文章目录第5层&#xff1a;应…

数据开源 | “白虎”数据集首批开源,迈出百万数据征途第一步

“白虎”数据集首批开源 在机器人智能不断迈向自主化、通用化的进程中&#xff0c;如何解决人形机器人的“喂养”难题、走出“数据荒漠”&#xff0c;已成为具身智能领域亟需攻克的关键课题。为此&#xff0c;2025 年初&#xff0c;全国首个异构人形机器人训练场在模力社区正式…

第17章——多元函数积分学的预备知识

文章目录思维导图场论初步方向导数梯度散度与旋度今日格言&#xff1a;如果凡事缺少了实行的勇气&#xff0c;再有智慧与仁爱也是枉然。思维导图 场论初步 场就是空间区域ΩΩΩ上的一种对应法则。可分为&#xff1a;数量场和向量场。 比如一个数量函数uu(x,y,z)uu(x,y,z)uu(x…

Vue》》Slot 插槽

插槽的概念 插槽就是子组件中的提供给父组件使用的一个占位符&#xff0c;用slot标签 表示&#xff0c;父组件可以在这个占位符中填充任何模板代码&#xff0c;如 HTML、组件等&#xff0c;填充的内容会替换子组件的slot标签。简单理解就是子组件中留下个“坑”&#xff0c;父组…

AKS部署.Net Shopping(K8S本地部署/AKS部署/key-value)

文章目录 项目地址 一、Api配置修改 1.1 配置docker 1. docker-compose配置环境变量 2. 修改appsettings 二、本地k8s部署 2.1 将本地镜像Push到dockerHub 2.2 制作K8S yaml文件 1. mogodb 2. shopping api 3. shoppingclient 3. port补充 4. Service 的type 三、部署到AKS 3.1…

vue3 el-table 去除小数

在 Vue 3 中使用 Element Plus 的 <el-table> 组件时&#xff0c;如果你希望去除表格列中的小数&#xff0c;你可以通过几种方式来实现&#xff1a;1. 使用 formatter 属性<el-table-column> 组件的 formatter 属性允许你自定义单元格的显示格式。你可以使用这个属…

JavaScript数组去重性能优化:Set与Object哈希表为何效率最高

文章目录 数组去重性能优化:为什么Set和Object哈希表的效率最高 引言 一、数组去重的常见方法 1.1 双重循环法 1.2 indexOf/includes方法 1.3 排序后相邻比较法 1.4 filter + indexOf方法 1.5 使用Set数据结构 1.6 使用Object哈希表 二、性能对比分析 2.1 时间复杂度对比 2.2 …

在VS Code中克隆项目失败

前提条件&#xff1a;电脑中已经安装好了Git一、错误原因&#xff1a;1、打开命令面板 快捷键 CtrlShiftP&#xff0c;输入Git: Clone 并回车&#xff1b;2、输入仓库URL回车后就发现报错了可以看到最后一行的报错内容&#xff1a;Git 无法找到或读取 SSL 证书文件&…

ASP.NET Core MVC中taghelper的ModelExpression详解

Microsoft.AspNetCore.Mvc.ViewFeatures.ModelExpression 是 ASP.NET Core MVC 框架中的一个核心类型&#xff0c;用于表示对模型属性的强类型引用。它在 Razor 视图、表单绑定和自定义 Tag Helper 中扮演关键角色&#xff0c;下面从技术细节、应用场景和最佳实践三个方面详细解…

楼宇 3D 建模:驱动建筑领域革新的数字力量

在科技飞速发展的当下&#xff0c;数字化技术正深刻改变着各个行业&#xff0c;建筑领域也不例外。楼宇 3D 建模作为关键的数字化技术&#xff0c;逐渐成为建筑设计、施工和管理过程中不可或缺的工具。它通过将现实中的楼宇以三维数字模型呈现&#xff0c;为建筑行业带来前所未…

打通视频到AI的第一公里:轻量RTSP服务如何重塑边缘感知入口?

在 AI 系统大规模部署、设备边缘化、数据实时化趋势下&#xff0c;视频能力的部署方式正迎来深刻变革。2025世界人工智能大会&#xff08;WAIC&#xff09;上&#xff0c;视频感知与智能决策之间的“连接效率”成为关键话题。而在这场连接能力的竞争中&#xff0c;轻量级、标准…

旅行短视频模糊的常见原因及应对方法

在旅行中拍摄短视频时&#xff0c;你是否经常遇到这样的问题&#xff1a;明明景色很美&#xff0c;但拍出来的视频却模糊不清&#xff0c;色彩暗淡&#xff0c;甚至噪点严重&#xff1f;尤其是在弱光环境&#xff08;如夜景、洞穴&#xff09;或快速移动&#xff08;如航拍、运…

【基础篇四】ASGI:Python异步Web的新标准

目录 一、ASGI规范深度解析 1.1 ASGI的诞生背景 1.2 ASGI核心概念 1.3 ASGI规范版本演进 二、WSGI vs ASGI&#xff1a;深度对比分析 2.1 架构模式对比 2.2 性能特性对比 2.3 适用场景分析 三、ASGI支持的协议类型 3.1 HTTP协议支持 3.1.1 HTTP处理流程 3.2 HTTP S…

51 单片机单文件多文件结构工程模板的创建教程

本章将详细介绍KEIL C51软件的使用方法及51单片机工程的创建流程。通过本章学习&#xff0c;掌握建立51单片机工程模板的技能&#xff0c;为后续51 单片机编程学习奠定基础。 单文件与多文件结构对比 对比项单文件结构多文件结构项目规模小项目、实验项目中大型项目、正式开发…

【Git】实用Git操作指南:从入门到高效协作

目录 1. Git 工作流程图 2. 基本配置 3. 获取本地仓库 4. 基础操作 5. 分支 6. Git远程仓库 6.1 常用的托管服务[远程仓库] 6.2 注册码云 6.3创建远程仓库 6.4 配置SSH公钥 6.5 操作远程仓库 7. 在IDEA中使用Git 附&#xff1a;铁令 下载地址&#xff1a;Git - Downloads 安装&…

InstructBLIP:通过指令微调迈向通用视觉-语言模型

温馨提示&#xff1a; 本篇文章已同步至"AI专题精讲" InstructBLIP&#xff1a;通过指令微调迈向通用视觉-语言模型 摘要 大规模预训练和指令微调在构建通用语言模型方面取得了显著成功。然而&#xff0c;构建通用视觉-语言模型仍然面临挑战&#xff0c;这主要源于…