本文目录导读:

我来为您创建一个完整的长轮询模拟实时数据获取的示例代码,这个示例将模拟服务器端和客户端的长轮询通信。
服务器端代码 (Node.js + Express)
// server.js
const express = require('express');
const app = express();
const cors = require('cors');
// 允许跨域请求
app.use(cors());
app.use(express.json());
// 模拟数据存储
let updateCount = 0;
const maxWaitTime = 30000; // 最大等待时间30秒
// 模拟数据源
function generateData() {
updateCount++;
return {
id: updateCount,
timestamp: new Date().toISOString(),
data: {
temperature: Math.random() * 30 + 20,
humidity: Math.random() * 40 + 40,
pressure: Math.random() * 10 + 1000,
speed: Math.random() * 100
},
message: `这是第 ${updateCount} 次数据更新`
};
}
// 模拟异步数据更新
function simulateAsyncDataUpdate(callback) {
// 随机延迟 1-8 秒模拟数据更新
const delay = Math.random() * 7000 + 1000;
setTimeout(() => {
callback(generateData());
}, delay);
}
// 长轮询接口
app.get('/api/poll', (req, res) => {
console.log(`[${new Date().toISOString()}] 收到长轮询请求`);
// 设置超时
const timeout = setTimeout(() => {
// 超时返回空响应
res.json({
status: 'timeout',
data: null,
message: '没有新数据,请继续轮询'
});
console.log(`[${new Date().toISOString()}] 请求超时`);
}, maxWaitTime);
// 模拟数据更新
simulateAsyncDataUpdate((newData) => {
clearTimeout(timeout);
// 返回新数据
res.json({
status: 'success',
data: newData,
message: '获取到新数据'
});
console.log(`[${new Date().toISOString()}] 发送新数据: `, newData.id);
});
});
// 批量数据接口
app.get('/api/batch', (req, res) => {
const batchSize = parseInt(req.query.size) || 5;
const batchData = [];
for (let i = 0; i < batchSize; i++) {
batchData.push(generateData());
}
res.json({
status: 'success',
data: batchData,
count: batchSize,
message: `返回 ${batchSize} 条数据`
});
});
// 启动服务器
const PORT = 3000;
app.listen(PORT, () => {
console.log(`长轮询服务器已启动,端口: ${PORT}`);
console.log(`访问 http://localhost:${PORT}/api/poll 进行长轮询`);
console.log(`访问 http://localhost:${PORT}/api/batch 获取批量数据`);
});
客户端代码 (HTML + JavaScript)
<!-- client.html -->
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">长轮询实时数据获取示例</title>
<style>
body {
font-family: Arial, sans-serif;
max-width: 800px;
margin: 20px auto;
padding: 20px;
background: #f5f5f5;
}
.container {
background: white;
border-radius: 8px;
padding: 20px;
box-shadow: 0 2px 4px rgba(0,0,0,0.1);
}
h1 {
color: #333;
text-align: center;
}
.status-bar {
display: flex;
justify-content: space-between;
align-items: center;
padding: 10px;
margin: 20px 0;
background: #e8f4fd;
border-radius: 6px;
}
.status {
color: #666;
}
.status.active {
color: #2ecc71;
}
.status.waiting {
color: #f39c12;
}
.status.error {
color: #e74c3c;
}
.controls {
display: flex;
gap: 10px;
justify-content: center;
margin: 20px 0;
}
button {
padding: 10px 20px;
border: none;
border-radius: 4px;
cursor: pointer;
font-size: 14px;
transition: background 0.3s;
}
.btn-start {
background: #2ecc71;
color: white;
}
.btn-start:hover {
background: #27ae60;
}
.btn-stop {
background: #e74c3c;
color: white;
}
.btn-stop:hover {
background: #c0392b;
}
.btn-start:disabled,
.btn-stop:disabled {
background: #ccc;
cursor: not-allowed;
}
.data-container {
margin: 20px 0;
}
.update-item {
background: #f9f9f9;
border-left: 4px solid #2ecc71;
padding: 15px;
margin: 10px 0;
border-radius: 0 6px 6px 0;
animation: slideIn 0.3s ease;
}
@keyframes slideIn {
from {
opacity: 0;
transform: translateY(-10px);
}
to {
opacity: 1;
transform: translateY(0);
}
}
.update-item .time {
color: #999;
font-size: 12px;
}
.update-item .id {
color: #666;
font-weight: bold;
}
.update-item .data {
margin: 10px 0;
padding: 10px;
background: #fff;
border-radius: 4px;
}
.stats {
display: grid;
grid-template-columns: repeat(4, 1fr);
gap: 10px;
margin: 20px 0;
}
.stat-item {
text-align: center;
padding: 15px;
background: #f0f0f0;
border-radius: 6px;
}
.stat-item .value {
font-size: 24px;
font-weight: bold;
color: #333;
}
.stat-item .label {
font-size: 12px;
color: #666;
margin-top: 5px;
}
.error-message {
background: #fee;
color: #c00;
padding: 10px;
border-radius: 4px;
margin: 10px 0;
display: none;
}
.max-updates-input {
margin: 20px 0;
display: flex;
gap: 10px;
align-items: center;
justify-content: center;
}
.max-updates-input input {
width: 60px;
padding: 8px;
border: 1px solid #ddd;
border-radius: 4px;
text-align: center;
}
</style>
</head>
<body>
<div class="container">
<h1>📊 实时数据长轮询获取</h1>
<div class="status-bar">
<div class="status" id="statusIndicator">未连接</div>
<div id="connectionInfo">连接次数: 0</div>
<button id="clearBtn" onclick="clearData()">清除数据</button>
</div>
<div class="max-updates-input">
<label for="maxUpdates">最大更新次数:</label>
<input type="number" id="maxUpdates" value="20" min="1" max="100">
<span style="color: #999; font-size: 12px;">(0 表示不限制)</span>
</div>
<div class="stats">
<div class="stat-item">
<div class="value" id="totalUpdates">0</div>
<div class="label">总更新次数</div>
</div>
<div class="stat-item">
<div class="value" id="totalRequests">0</div>
<div class="label">总请求次数</div>
</div>
<div class="stat-item">
<div class="value" id="successRate">0%</div>
<div class="label">成功率</div>
</div>
<div class="stat-item">
<div class="value" id="avgResponseTime">0ms</div>
<div class="label">平均响应时间</div>
</div>
</div>
<div class="controls">
<button id="startBtn" class="btn-start" onclick="startPolling()">开始轮询</button>
<button id="stopBtn" class="btn-stop" onclick="stopPolling()" disabled>停止轮询</button>
</div>
<div class="error-message" id="errorMessage"></div>
<div class="data-container" id="dataContainer">
<h3>实时数据更新</h3>
<p style="color: #999;">点击"开始轮询"按钮开始获取实时数据...</p>
</div>
</div>
<script>
class LongPollingClient {
constructor(options = {}) {
this.serverUrl = options.serverUrl || 'http://localhost:3000';
this.isPolling = false;
this.currentRequest = null;
this.updateCount = 0;
this.requestCount = 0;
this.successCount = 0;
this.failCount = 0;
this.totalResponseTime = 0;
this.startTime = null;
// DOM元素引用
this.statusIndicator = document.getElementById('statusIndicator');
this.dataContainer = document.getElementById('dataContainer');
this.totalUpdates = document.getElementById('totalUpdates');
this.totalRequests = document.getElementById('totalRequests');
this.successRate = document.getElementById('successRate');
this.avgResponseTime = document.getElementById('avgResponseTime');
this.connectionInfo = document.getElementById('connectionInfo');
this.errorMessage = document.getElementById('errorMessage');
this.startBtn = document.getElementById('startBtn');
this.stopBtn = document.getElementById('stopBtn');
}
setStatus(status, message) {
this.statusIndicator.className = `status ${status}`;
this.statusIndicator.textContent = message;
}
showError(message) {
this.errorMessage.style.display = 'block';
this.errorMessage.textContent = message;
setTimeout(() => {
this.errorMessage.style.display = 'none';
}, 3000);
}
updateStats() {
this.totalUpdates.textContent = this.updateCount;
this.totalRequests.textContent = this.requestCount;
const total = this.successCount + this.failCount;
this.successRate.textContent = total > 0 ?
`${Math.round((this.successCount / total) * 100)}%` : '0%';
this.avgResponseTime.textContent = this.requestCount > 0 ?
`${Math.round(this.totalResponseTime / this.requestCount)}ms` : '0ms';
this.connectionInfo.textContent = `连接次数: ${this.requestCount}`;
}
addDataItem(data) {
if (this.dataContainer.querySelector('p')) {
this.dataContainer.innerHTML = '<h3>实时数据更新</h3>';
}
const item = document.createElement('div');
item.className = 'update-item';
const sensors = data.data || {};
const sensorHtml = Object.entries(sensors)
.map(([key, value]) => `<strong>${key}:</strong> ${value.toFixed(2)}`)
.join(' | ');
item.innerHTML = `
<div class="time">${data.timestamp}</div>
<div class="id">#${data.id} - ${data.message}</div>
<div class="data">${sensorHtml}</div>
`;
this.dataContainer.insertBefore(item, this.dataContainer.firstChild.nextSibling);
// 限制显示数量
const items = this.dataContainer.querySelectorAll('.update-item');
if (items.length > 50) {
items[items.length - 1].remove();
}
this.updateCount++;
this.updateStats();
}
async longPoll() {
if (!this.isPolling) return;
const maxUpdates = parseInt(document.getElementById('maxUpdates').value) || 0;
if (maxUpdates > 0 && this.updateCount >= maxUpdates) {
this.stopPolling();
this.setStatus('success', '已达到最大更新次数');
return;
}
this.setStatus('waiting', '等待新数据...');
try {
const startTime = Date.now();
this.requestCount++;
const response = await fetch(`${this.serverUrl}/api/poll`, {
method: 'GET',
headers: {
'Accept': 'application/json'
}
});
const responseTime = Date.now() - startTime;
this.totalResponseTime += responseTime;
if (!response.ok) {
throw new Error(`HTTP错误: ${response.status}`);
}
const result = await response.json();
if (result.status === 'success' && result.data) {
this.successCount++;
this.setStatus('active', '收到新数据');
this.addDataItem(result.data);
} else if (result.status === 'timeout') {
// 超时是正常的,继续轮询
console.log('请求超时,继续轮询');
}
} catch (error) {
this.failCount++;
console.error('长轮询错误:', error);
this.showError(`请求失败: ${error.message}`);
this.setStatus('error', '连接错误');
// 错误后等待一段时间再重试
await new Promise(resolve => setTimeout(resolve, 3000));
}
// 更新统计信息
this.updateStats();
// 继续轮询
if (this.isPolling) {
this.longPoll();
}
}
startPolling() {
if (this.isPolling) return;
this.isPolling = true;
this.startTime = Date.now();
this.startBtn.disabled = true;
this.stopBtn.disabled = false;
this.setStatus('waiting', '开始轮询...');
this.longPoll();
}
stopPolling() {
this.isPolling = false;
this.startBtn.disabled = false;
this.stopBtn.disabled = true;
this.setStatus('success', '已停止轮询');
}
clearData() {
this.dataContainer.innerHTML = '<h3>实时数据更新</h3><p style="color: #999;">数据已清空,点击"开始轮询"重新获取...</p>';
this.updateCount = 0;
this.requestCount = 0;
this.successCount = 0;
this.failCount = 0;
this.totalResponseTime = 0;
this.updateStats();
}
}
// 初始化
const client = new LongPollingClient({
serverUrl: 'http://localhost:3000'
});
// 全局函数
function startPolling() {
client.startPolling();
}
function stopPolling() {
client.stopPolling();
}
function clearData() {
stopPolling();
client.clearData();
}
</script>
</body>
</html>
使用说明
安装依赖
npm init -y npm install express cors
运行服务器
node server.js
打开客户端
直接在浏览器中打开 client.html 文件
功能说明
服务器端特性:
- 模拟随机延迟 (1-8秒) 的数据更新
- 30秒超时机制
- 批量数据获取接口
- CORS跨域支持
客户端特性:
- 实时状态显示
- 统计数据跟踪 (更新次数、请求次数、成功率、平均响应时间)
- 支持设置最大更新次数
- 错误重试机制
- 数据可视化显示
- 自动清理过多数据
核心要点
长轮询工作原理:
- 客户端发起HTTP请求
- 服务器保持连接打开
- 当有新数据时立即响应
- 超时时返回空响应
- 客户端收到响应后立即发起新请求
优势:
- 实时性较好
- 实现简单
- 兼容性好
适用场景:
- 实时通知系统
- 数据监控面板
- 即时消息推送
- 在线协作工具
这个示例提供了完整的长轮询实现,包括服务器端和客户端代码,可以直接运行测试。
版权声明:NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
足球直播
足球直播
足球直播
篮球直播
篮球直播
篮球直播
NBA直播
NBA直播
NBA直播
CBA直播
CBA直播
CBA直播
亚冠直播
亚冠直播
亚冠直播
欧冠直播
欧冠直播
欧冠直播
英超直播
英超直播
英超直播
西甲直播
西甲直播
西甲直播
中超直播
中超直播
中超直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
曼联直播
曼联直播
曼联直播
日职联直播
日职联直播
日职联直播
澳超直播
澳超直播
澳超直播
阿森纳直播
阿森纳直播
阿森纳直播
足球直播
足球直播
足球直播
NBA直播
NBA直播
NBA直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
欧冠直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
足球直播
足球直播
足球直播
足球直播
足球直播
英超直播
英超直播
英超直播
英超直播
英超直播
篮球直播
篮球直播
篮球直播
篮球直播
篮球直播
CBA直播
CBA直播
CBA直播
CBA直播
CBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
NBA直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播
中超直播