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
英文地址,請注明出處:http://en.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;這主要源于…