如何用PHP的Ratchet WebSocket连接Bybit并在HTML端展示实时JSON数据
实现实时订单簿数据前端展示方案
一、重构后端:搭建PHP WebSocket中转服务
原代码仅能在后端打印数据,需改成WebSocket中转服务,将Bybit的实时数据转发给前端客户端。
1. 安装依赖
确保已安装Ratchet,若未安装执行:
composer require cboden/ratchet
2. 编写中转服务代码(OrderbookRelay.php)
<?php use Ratchet\MessageComponentInterface; use Ratchet\ConnectionInterface; use WebSocket\Client as BybitClient; require 'vendor/autoload.php'; class OrderbookRelay implements MessageComponentInterface { protected $clients; protected $bybitClient; public function __construct() { $this->clients = new \SplObjectStorage; // 连接Bybit测试网WebSocket $this->bybitClient = new BybitClient("wss://stream-testnet.bybit.com/contract/usdt/public/v3", ['timeout' => 300]); // 订阅目标交易对订单簿 $subscribeMsg = json_encode([ "req_id" => "100001", "op" => "subscribe", "args" => [ "orderbook.1.BTCUSDT", "orderbook.1.ETHUSDT", "orderbook.1.ADAUSDT", "orderbook.1.TRXUSDT", "orderbook.1.XRPUSDT", "orderbook.1.BNBUSDT", "orderbook.1.SOLUSDT", "orderbook.1.ARBUSDT", "orderbook.1.DOGEUSDT", "orderbook.1.C98USDT" ] ]); $this->bybitClient->text($subscribeMsg); // 异步接收Bybit数据并广播给前端 $this->startBybitListener(); } protected function startBybitListener() { $that = $this; \React\Async\async(function() use ($that) { while ($that->bybitClient->isConnected()) { try { $message = $that->bybitClient->receive(); // 广播消息给所有连接的前端 foreach ($that->clients as $client) { $client->send($message); } } catch (\WebSocket\ConnectionException $e) { error_log("Bybit连接错误: " . $e->getMessage()); // 自动重连 $that->bybitClient = new BybitClient("wss://stream-testnet.bybit.com/contract/usdt/public/v3", ['timeout' => 300]); $subscribeMsg = json_encode([ "req_id" => "100001", "op" => "subscribe", "args" => [ "orderbook.1.BTCUSDT", "orderbook.1.ETHUSDT", "orderbook.1.ADAUSDT", "orderbook.1.TRXUSDT", "orderbook.1.XRPUSDT", "orderbook.1.BNBUSDT", "orderbook.1.SOLUSDT", "orderbook.1.ARBUSDT", "orderbook.1.DOGEUSDT", "orderbook.1.C98USDT" ] ]); $that->bybitClient->text($subscribeMsg); } } })(); } public function onOpen(ConnectionInterface $conn) { $this->clients->attach($conn); echo "新客户端连接: {$conn->resourceId}\n"; } public function onMessage(ConnectionInterface $from, $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服务,监听8080端口 $server = \Ratchet\Server\IoServer::factory( new \Ratchet\Http\HttpServer( new \Ratchet\WebSocket\WsServer( new OrderbookRelay() ) ), 8080 ); $server->run();
二、编写前端HTML页面(orderbook.html)
页面通过WebSocket连接本地服务,接收数据并实时渲染订单簿:
<!DOCTYPE html> <html lang="zh-CN"> <head> <meta charset="UTF-8"> <title>Bybit测试网实时订单簿</title> <style> .orderbook-container { display: flex; gap: 20px; flex-wrap: wrap; padding: 20px; } .pair-card { border: 1px solid #eee; border-radius: 8px; padding: 15px; width: 300px; box-shadow: 0 2px 4px rgba(0,0,0,0.1); } .pair-title { font-weight: bold; font-size: 18px; margin-bottom: 10px; color: #333; } .orderbook-side { margin-bottom: 15px; } .side-title { font-size: 14px; color: #666; margin-bottom: 5px; } .price-level { display: flex; justify-content: space-between; font-size: 12px; padding: 2px 0; } .bid { color: #00c48c; } .ask { color: #ff4d4f; } </style> </head> <body> <h1>Bybit测试网实时订单簿数据</h1> <div class="orderbook-container" id="orderbookContainer"></div> <script> // 连接本地WebSocket服务 const ws = new WebSocket('ws://localhost:8080'); // 存储各交易对订单簿数据 const orderbookData = {}; ws.onopen = () => console.log('已连接到订单簿中继服务'); ws.onmessage = (event) => { const data = JSON.parse(event.data); // 仅处理订单簿更新消息 if (data.topic?.startsWith('orderbook.')) { const pair = data.topic.split('.')[2]; orderbookData[pair] = data.data; renderOrderbook(); } }; ws.onerror = (error) => console.error('WebSocket连接错误:', error); ws.onclose = () => { console.log('WebSocket连接已关闭,3秒后尝试重连'); setTimeout(() => window.location.reload(), 3000); }; // 渲染订单簿到页面 function renderOrderbook() { const container = document.getElementById('orderbookContainer'); container.innerHTML = ''; Object.keys(orderbookData).forEach(pair => { const book = orderbookData[pair]; const card = document.createElement('div'); card.className = 'pair-card'; // 交易对标题 const title = document.createElement('div'); title.className = 'pair-title'; title.textContent = pair; card.appendChild(title); // 买单区域 const bidSide = document.createElement('div'); bidSide.className = 'orderbook-side'; const bidTitle = document.createElement('div'); bidTitle.className = 'side-title'; bidTitle.textContent = '买单(Bids)'; bidSide.appendChild(bidTitle); book.b.forEach(bid => { const level = document.createElement('div'); level.className = 'price-level bid'; level.innerHTML = `<span>${bid[0]}</span><span>${bid[1]}</span>`; bidSide.appendChild(level); }); card.appendChild(bidSide); // 卖单区域 const askSide = document.createElement('div'); askSide.className = 'orderbook-side'; const askTitle = document.createElement('div'); askTitle.className = 'side-title'; askTitle.textContent = '卖单(Asks)'; askSide.appendChild(askTitle); book.a.forEach(ask => { const level = document.createElement('div'); level.className = 'price-level ask'; level.innerHTML = `<span>${ask[0]}</span><span>${ask[1]}</span>`; askSide.appendChild(level); }); card.appendChild(askSide); container.appendChild(card); }); } </script> </body> </html>
三、运行步骤
- 启动后端中继服务:
php OrderbookRelay.php
- 在浏览器中打开
orderbook.html页面,即可看到实时更新的订单簿数据。
关键说明
- 后端服务作为中转,负责连接Bybit并将数据广播给所有前端客户端
- 前端通过WebSocket建立长连接,接收数据后动态渲染页面
- 页面支持自动重连,若服务断开会尝试刷新页面恢复连接
内容的提问来源于stack exchange,提问作者Travis
相关产品推荐
相关产品推荐

