轮询与 WebSocket 对比
短轮询、长轮询和 WebSocket 三种实时通信方案的区别与实现
问题
对比短轮询、长轮询 (Long Polling) 与 WebSocket 三种实时通信方案。
解答
短轮询 (Short Polling)
客户端定时向服务器发送请求,无论是否有新数据。
// 客户端:每 3 秒请求一次
function shortPolling() {
setInterval(async () => {
const response = await fetch('/api/messages')
const data = await response.json()
if (data.length > 0) {
console.log('新消息:', data)
}
}, 3000)
}
特点:实现简单,但浪费资源,实时性差。
长轮询 (Long Polling)
服务器收到请求后不立即响应,等到有新数据或超时才返回。
// 客户端:递归请求
async function longPolling() {
try {
const response = await fetch('/api/messages/subscribe')
const data = await response.json()
console.log('收到数据:', data)
} catch (error) {
console.error('连接错误:', error)
}
// 无论成功失败,立即发起下一次请求
longPolling()
}
// 服务端 (Node.js + Express)
app.get('/api/messages/subscribe', (req, res) => {
const checkForMessages = () => {
const messages = getNewMessages()
if (messages.length > 0) {
res.json(messages)
} else {
// 没有消息,等待 1 秒后再检查,最多等 30 秒
setTimeout(checkForMessages, 1000)
}
}
// 30 秒超时
setTimeout(() => res.json([]), 30000)
checkForMessages()
})
特点:比短轮询实时性好,但仍是单向通信,每次需要重新建立连接。
WebSocket
全双工通信协议,建立持久连接后双方可随时发送数据。
// 客户端
const ws = new WebSocket('ws://localhost:8080')
ws.onopen = () => {
console.log('连接已建立')
ws.send(JSON.stringify({ type: 'subscribe', channel: 'chat' }))
}
ws.onmessage = (event) => {
const data = JSON.parse(event.data)
console.log('收到消息:', data)
}
ws.onclose = () => {
console.log('连接已关闭')
// 可以在这里实现重连逻辑
}
ws.onerror = (error) => {
console.error('WebSocket 错误:', error)
}
// 发送消息
function sendMessage(message) {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify(message))
}
}
// 服务端 (Node.js + ws 库)
const WebSocket = require('ws')
const wss = new WebSocket.Server({ port: 8080 })
wss.on('connection', (ws) => {
console.log('客户端已连接')
ws.on('message', (message) => {
const data = JSON.parse(message)
console.log('收到:', data)
// 广播给所有客户端
wss.clients.forEach((client) => {
if (client.readyState === WebSocket.OPEN) {
client.send(JSON.stringify({ type: 'broadcast', data }))
}
})
})
ws.on('close', () => {
console.log('客户端已断开')
})
})
对比表格
| 特性 | 短轮询 | 长轮询 | WebSocket |
|---|---|---|---|
| 实时性 | 差 | 中 | 高 |
| 服务器压力 | 高 | 中 | 低 |
| 通信方向 | 单向 | 单向 | 双向 |
| 连接方式 | 每次新建 | 每次新建 | 持久连接 |
| 实现复杂度 | 低 | 中 | 中 |
| 兼容性 | 好 | 好 | 需要支持 |
使用场景
// 短轮询:适合更新频率低、实时性要求不高的场景
// 例如:检查是否有新版本
setInterval(() => checkForUpdates(), 60000)
// 长轮询:适合兼容性要求高、消息频率不高的场景
// 例如:邮件通知
// WebSocket:适合高实时性、双向通信的场景
// 例如:聊天室、实时协作、游戏、股票行情
关键点
- 短轮询:定时请求,实现简单但浪费带宽,延迟等于轮询间隔
- 长轮询:服务器 hold 住请求直到有数据,减少无效请求,但仍需频繁建立连接
- WebSocket:基于 TCP 的全双工协议,一次握手后保持连接,适合高频实时通信
- 选择依据:根据实时性要求、消息频率、兼容性需求选择合适方案
- WebSocket 注意:需要处理心跳保活、断线重连、消息确认等问题
目录