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. 状态同步: 多组件间的连接状态同步

相关推荐
江城开朗的豌豆44 分钟前
从生命周期到useEffect:我的React函数组件进化之旅
前端·javascript·react.js
江城开朗的豌豆1 小时前
React组件传值:轻松掌握React组件通信秘籍
前端·javascript·react.js
Sailing1 小时前
别再放任用户乱填 IP 了!一套前端 IP 与 CIDR 校验的高效方案
前端·javascript·面试
遂心_15 小时前
JavaScript 函数参数传递机制:一道经典面试题解析
前端·javascript
遂心_16 小时前
深入理解 React Hook:useEffect 完全指南
前端·javascript·react.js
前端Hardy16 小时前
HTML&CSS: 谁懂啊!用代码 “擦去”图片雾气
前端·javascript·css
前端Hardy16 小时前
HTML&CSS:好精致的导航栏
前端·javascript·css
一个不爱写代码的瘦子17 小时前
迭代器和生成器
前端·javascript
源猿人20 小时前
企业级文件浏览系统的Vue实现:架构设计与最佳实践
前端·javascript·数据可视化
RoyLin20 小时前
TypeScript设计模式:迭代器模式
javascript·后端·node.js