You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

如何用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>

三、运行步骤

  1. 启动后端中继服务:
php OrderbookRelay.php
  1. 在浏览器中打开orderbook.html页面,即可看到实时更新的订单簿数据。

关键说明

  • 后端服务作为中转,负责连接Bybit并将数据广播给所有前端客户端
  • 前端通过WebSocket建立长连接,接收数据后动态渲染页面
  • 页面支持自动重连,若服务断开会尝试刷新页面恢复连接

内容的提问来源于stack exchange,提问作者Travis

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.07.25 19:54:58