WebSocket实时通信系统——js技能提升

2. WebSocket实时通信系统

功能概述

实现完整的WebSocket通信系统,支持实时消息推送、连接管理、心跳检测和自动重连。

技术难点

  • WebSocket连接生命周期管理
  • 消息序列化和反序列化
  • 心跳机制和连接保活
  • 错误处理和重连策略
  • 多组件状态同步

实现思路

2.1 WebSocket管理器
typescript 复制代码
// src/utils/websocket.ts
export interface WebSocketOptions {
  url: string
  onConnect?: () => void
  onDisconnect?: () => void
  onMessage?: (message: any) => void
  onError?: (error: any) => void
}

export class WebSocketManager {
  private ws: WebSocket | null = null
  private options: WebSocketOptions
  private reconnectAttempts = 0
  private maxReconnectAttempts = 5
  private reconnectInterval = 1000
  private heartbeatInterval: NodeJS.Timeout | null = null

  constructor(options: WebSocketOptions) {
    this.options = options
  }

  // 连接WebSocket
  connect(): Promise<void> {
    return new Promise((resolve, reject) => {
      try {
        this.ws = new WebSocket(this.options.url)
        
        this.ws.onopen = () => {
          console.log('✅ WebSocket 连接成功')
          this.reconnectAttempts = 0
          this.startHeartbeat()
          this.options.onConnect?.()
          resolve()
        }

        this.ws.onclose = (event) => {
          console.log('🔌 WebSocket 连接断开:', event.code, event.reason)
          this.stopHeartbeat()
          this.options.onDisconnect?.()
          
          // 自动重连逻辑
          if (this.reconnectAttempts < this.maxReconnectAttempts) {
            this.reconnectAttempts++
            setTimeout(() => {
              this.connect().catch(console.error)
            }, this.reconnectInterval * this.reconnectAttempts)
          }
        }

        this.ws.onerror = error => {
          console.error('❌ WebSocket 错误:', error)
          this.options.onError?.(error)
          reject(error)
        }

        this.ws.onmessage = event => {
          try {
            const data = JSON.parse(event.data)
            this.options.onMessage?.(data)
          } catch (err) {
            console.warn('⚠ 消息解析失败:', event.data)
            this.options.onMessage?.(event.data)
          }
        }
      } catch (error) {
        console.error('❌ WebSocket 连接失败:', error)
        reject(error)
      }
    })
  }

  // 发送消息
  send(data: any): void {
    if (this.ws && this.ws.readyState === WebSocket.OPEN) {
      const message = typeof data === 'string' ? data : JSON.stringify(data)
      this.ws.send(message)
    } else {
      throw new Error('WebSocket未连接')
    }
  }

  // 心跳机制
  private startHeartbeat(): void {
    this.heartbeatInterval = setInterval(() => {
      if (this.ws && this.ws.readyState === WebSocket.OPEN) {
        this.send({ type: 'heartbeat', timestamp: Date.now() })
      }
    }, 30000) // 30秒发送一次心跳
  }

  private stopHeartbeat(): void {
    if (this.heartbeatInterval) {
      clearInterval(this.heartbeatInterval)
      this.heartbeatInterval = null
    }
  }

  // 断开连接
  disconnect(): void {
    this.stopHeartbeat()
    if (this.ws) {
      this.ws.close()
      this.ws = null
    }
  }

  // 获取连接状态
  get isConnected(): boolean {
    return this.ws?.readyState === WebSocket.OPEN
  }
}
2.2 Vue组合式API封装
typescript 复制代码
// src/hooks/useWebSocket.ts
export function useWebSocket(options: WSOptions) {
  const socketManager = ref<WebSocketManager | null>(null)
  const isConnected = ref(false)

  const connect = async (params?: WSOptions) => {
    if (!useCookie('token').value) return
    
    const innerParams = params || options
    socketManager.value = createWebSocketManager(
      typeof innerParams === 'function' ? innerParams() : innerParams
    )
    
    try {
      await socketManager.value?.connect()
      isConnected.value = true
      
      // 设置断开连接回调
      socketManager.value?.setDisconnectCallback(() => {
        isConnected.value = false
        const { message } = useGlobalComponent()
        message.error('网络连接断开,请刷新页面')
      })
    } catch (error) {
      console.error('WebSocket连接失败:', error)
      isConnected.value = false
    }
  }

  // 发送消息
  const sendMessage = <T>(data: T) => {
    if (!socketManager.value?.isConnected) {
      const { message } = useGlobalComponent()
      message.error('网络连接断开,请刷新页面')
      return
    }
    socketManager.value?.send(data)
  }

  const disconnect = () => {
    socketManager.value?.disconnect()
    isConnected.value = false
  }

  onUnmounted(disconnect)

  return {
    sendMessage,
    connect,
    disconnect,
    isConnected: () => isConnected.value,
  }
}
2.3 聊天状态管理集成
typescript 复制代码
// src/stores/chat/methods.ts
export const useChatStore = defineStore('chat', () => {
  const { sendMessage, connect, disconnect, isConnected } = useWebSocket(
    () => ({
      url: `ws://192.168.201.201:8088/api/websocket?token=${useCookie('token').value}`,
      onMessage: msg => {
        if (msg.event_id !== state.list[state.list.length - 1]?.event_id) return
        state.list = onMessage(msg, state.list)
        
        if (
          state.isFirst &&
          [NotificationType.FINISH, NotificationType.END].includes(
            msg?.data?.type
          )
        ) {
          getChatSummary(msg.data.session_id).then(res => {
            state.title = res
            state.isFirst = false
          })
        }
      },
    })
  )

  const send = (data: string, id: number) => {
    const lastMsg = state.list[state.list.length - 1]
    let callerInstanceId = ''
    
    if (lastMsg && 'caller' in lastMsg) {
      callerInstanceId = (lastMsg?.caller as { instance_id: string })?.instance_id
    }
    
    const msg = createUserMessage(data, id, callerInstanceId)
    const question = createResponseMessage(data, id, callerInstanceId)
    
    if (state.list.length) {
      if (state.list[state.list.length - 1]?.session_id === id) {
        state.list = [...state.list, question]
      } else {
        state.list = [question]
        state.isFirst = true
      }
    } else {
      state.list = [question]
      state.isFirst = true
    }
    
    sendMessage(msg)
    return question
  }

  return {
    send,
    isConnected,
  }
})

关键技术点

  1. 连接管理: 完整的连接生命周期管理
  2. 自动重连: 指数退避重连策略
  3. 心跳机制: 保持连接活跃状态
  4. 错误处理: 完善的错误捕获和用户提示
  5. 状态同步: 多组件间的连接状态同步

相关推荐
jump_jump4 小时前
妙用 localeCompare 获取汉字拼音首字母
前端·javascript·浏览器
U.2 SSD4 小时前
Echarts单轴坐标系散点图
前端·javascript·echarts
不做无法实现的梦~4 小时前
jetson刷系统之后没有浏览器--解决办法
开发语言·javascript·ecmascript
Jedi Hongbin4 小时前
Three.js NodeMaterial 节点材质系统文档
前端·javascript·three.js·nodematerial
前端小马5 小时前
前后端Long类型ID精度丢失问题
java·前端·javascript·后端
十碗饭吃不饱6 小时前
WebClient工具调用HTTP接口报错远程主机断开连接
网络·网络协议·http
liu****6 小时前
基于websocket的多用户网页五子棋(九)
服务器·网络·数据库·c++·websocket·网络协议·个人开发
心态特好6 小时前
详解WebSocket及其妙用
java·python·websocket·网络协议
liu****6 小时前
基于websocket的多用户网页五子棋(八)
服务器·前端·javascript·数据库·c++·websocket·个人开发
San306 小时前
深入理解 JavaScript 函数:从基础到高阶应用
前端·javascript·node.js