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()

适用场景

同分类其他题目