118 lines
3.3 KiB
PHP
Executable File
118 lines
3.3 KiB
PHP
Executable File
#!/usr/bin/env php
|
||
<?php
|
||
/**
|
||
* NAC 量子浏览器 WebSocket 服务
|
||
* 使用 Workerman 实现,监听端口 9553
|
||
* 每 3 秒轮询 Explorer API,有新区块则广播给所有在线客户端
|
||
*
|
||
* 启动:php ws_server.php start -d
|
||
* 停止:php ws_server.php stop
|
||
* 重启:php ws_server.php restart
|
||
*/
|
||
|
||
require_once __DIR__ . '/vendor/autoload.php';
|
||
|
||
use Workerman\Worker;
|
||
use Workerman\Timer;
|
||
|
||
// Explorer API 地址(服务端调用,不暴露给前端)
|
||
define('EXPLORER_API', 'http://127.0.0.1:9551');
|
||
|
||
// WebSocket 服务,监听 9553 端口
|
||
$wsWorker = new Worker('websocket://0.0.0.0:9553');
|
||
$wsWorker->count = 1; // 单进程足够
|
||
$wsWorker->name = 'nac-quantum-ws';
|
||
$wsWorker->user = 'www';
|
||
|
||
// 记录最新区块高度
|
||
$latestBlock = 0;
|
||
|
||
/**
|
||
* 调用 Explorer API
|
||
*/
|
||
function callApi(string $path): ?array
|
||
{
|
||
$ch = curl_init();
|
||
curl_setopt_array($ch, [
|
||
CURLOPT_URL => EXPLORER_API . $path,
|
||
CURLOPT_RETURNTRANSFER => true,
|
||
CURLOPT_TIMEOUT => 3,
|
||
CURLOPT_CONNECTTIMEOUT => 2,
|
||
CURLOPT_HTTPHEADER => ['Accept: application/json'],
|
||
]);
|
||
$body = curl_exec($ch);
|
||
$status = curl_getinfo($ch, CURLINFO_HTTP_CODE);
|
||
curl_close($ch);
|
||
|
||
if ($body === false || $status < 200 || $status >= 300) {
|
||
return null;
|
||
}
|
||
$json = json_decode($body, true);
|
||
return is_array($json) ? $json : null;
|
||
}
|
||
|
||
$wsWorker->onWorkerStart = function ($worker) use (&$latestBlock) {
|
||
// 初始化最新区块高度
|
||
$res = callApi('/api/v1/blocks/latest');
|
||
if ($res && isset($res['data']['number'])) {
|
||
$latestBlock = (int) $res['data']['number'];
|
||
}
|
||
|
||
// 每 3 秒轮询一次
|
||
Timer::add(3, function () use ($worker, &$latestBlock) {
|
||
$res = callApi('/api/v1/blocks/latest');
|
||
if (!$res || !isset($res['data']['number'])) {
|
||
return;
|
||
}
|
||
|
||
$newHeight = (int) $res['data']['number'];
|
||
if ($newHeight <= $latestBlock) {
|
||
return;
|
||
}
|
||
|
||
// 有新区块,广播给所有在线客户端
|
||
$latestBlock = $newHeight;
|
||
$block = $res['data'];
|
||
|
||
$payload = json_encode([
|
||
'type' => 'new_block',
|
||
'block' => [
|
||
'number' => $block['number'] ?? 0,
|
||
'hash' => $block['hash'] ?? '',
|
||
'timestamp' => $block['timestamp'] ?? time(),
|
||
'txCount' => $block['txCount'] ?? 0,
|
||
'validator' => $block['validator'] ?? '',
|
||
'size' => $block['size'] ?? 0,
|
||
],
|
||
], JSON_UNESCAPED_UNICODE);
|
||
|
||
foreach ($worker->connections as $conn) {
|
||
$conn->send($payload);
|
||
}
|
||
});
|
||
};
|
||
|
||
$wsWorker->onConnect = function ($conn) {
|
||
// 新客户端连接时,发送当前最新区块高度
|
||
global $latestBlock;
|
||
$conn->send(json_encode([
|
||
'type' => 'connected',
|
||
'latestBlock' => $latestBlock,
|
||
'server' => 'NAC Quantum Browser WebSocket v1.0',
|
||
]));
|
||
};
|
||
|
||
$wsWorker->onMessage = function ($conn, $data) {
|
||
// 客户端可发送 ping,服务端回 pong
|
||
if (trim($data) === 'ping') {
|
||
$conn->send('pong');
|
||
}
|
||
};
|
||
|
||
$wsWorker->onError = function ($conn, $code, $msg) {
|
||
// 静默处理连接错误
|
||
};
|
||
|
||
// 启动
|
||
Worker::runAll();
|