WebSocket 和 HTTP 的区别?如何使用 WebSocket?
WebSocket vs HTTP
| 特性 | HTTP | WebSocket |
|---|---|---|
| 通信方式 | 请求-响应 | 全双工 |
| 连接 | 短连接(HTTP/1.1 keep-alive) | 长连接 |
| 方向 | 单向(客户端→服务器) | 双向 |
| 协议 | http/https | ws/wss |
| 数据格式 | 文本 | 文本/二进制 |
| 开销 | 每次带完整 header | 握手后数据帧开销极小 |
WebSocket 使用
客户端:
// 创建连接
const ws = new WebSocket('ws://localhost:8080')
// 或加密
const ws = new WebSocket('wss://localhost:8080')
// 事件
ws.onopen = () => {
console.log('连接已建立')
ws.send('Hello Server')
ws.send(JSON.stringify({ type: 'message', data: 'hello' }))
}
ws.onmessage = (e) => {
console.log('收到:', e.data)
}
ws.onerror = (e) => {
console.log('错误:', e)
}
ws.onclose = (e) => {
console.log('连接关闭:', e.code, e.reason)
}
// 主动关闭
ws.close(1000, '正常关闭')服务器(Node.js + ws 库):
const { WebSocketServer } = require('ws')
const wss = new WebSocketServer({ port: 8080 })
wss.on('connection', (ws, req) => {
console.log('新连接:', req.socket.remoteAddress)
ws.on('message', (data) => {
console.log('收到:', data.toString())
// 广播给所有客户端
wss.clients.forEach(client => {
if (client.readyState === WebSocket.OPEN) {
client.send(data.toString())
}
})
})
ws.on('close', () => {
console.log('连接关闭')
})
})心跳机制
// 客户端心跳
let heartbeatInterval
ws.onopen = () => {
heartbeatInterval = setInterval(() => {
if (ws.readyState === WebSocket.OPEN) {
ws.send(JSON.stringify({ type: 'ping' }))
}
}, 30000) // 30秒
}
ws.onclose = () => {
clearInterval(heartbeatInterval)
// 自动重连
setTimeout(reconnect, 3000)
}
// 服务器响应心跳
ws.on('message', (data) => {
const msg = JSON.parse(data)
if (msg.type === 'ping') {
ws.send(JSON.stringify({ type: 'pong' }))
return
}
})自动重连
class ReconnectingWebSocket {
constructor(url, options = {}) {
this.url = url
this.maxRetry = options.maxRetry || 5
this.retryCount = 0
this.connect()
}
connect() {
this.ws = new WebSocket(this.url)
this.ws.onopen = () => {
this.retryCount = 0
console.log('Connected')
}
this.ws.onclose = () => {
if (this.retryCount < this.maxRetry) {
this.retryCount++
const delay = Math.min(1000 * 2 ** this.retryCount, 30000)
setTimeout(() => this.connect(), delay)
}
}
this.ws.onmessage = (e) => this.onMessage?.(e.data)
}
send(data) {
if (this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data)
}
}
}WebSocket vs SSE vs 轮询
| 方式 | 方向 | 开销 | 适合场景 |
|---|---|---|---|
| 轮询 | 单向 | 大 | 简单场景 |
| 长轮询 | 单向 | 中 | 兼容性好 |
| SSE | 服务器→客户端 | 小 | 通知/推送 |
| WebSocket | 双向 | 最小 | 聊天/实时 |
SSE 替代方案
如果只需要服务器推送(不需要客户端→服务器),SSE 更简单:
const source = new EventSource('/api/stream')
source.onmessage = (e) => {
console.log(e.data)
}
source.onerror = () => source.close()适用场景
- ✅ 聊天应用
- ✅ 实时游戏
- ✅ 协同编辑
- ✅ 实时数据推送
- ❌ 简单的请求-响应(用 HTTP 即可)