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

相关推荐
跨境小新24 分钟前
手机移动代理IP:使用、配置、维护的10问10答
网络协议·tcp/ip·智能手机
掘金安东尼1 小时前
Rspack 推出 Rslint:一个用 Go 编写的 TypeScript-First Linter
前端·javascript·github
蓝胖子的小叮当2 小时前
JavaScript基础(十四)字符串方法总结
前端·javascript
墨雨听阁3 小时前
8.26网络编程——Modbus TCP
网络·网络协议·学习·tcp/ip
weixin_541299944 小时前
VSCode: 从插件安装到配置,如何实现 Ctrl+S 保存时,完全按照 .eslintrc.js 中的 ESLint 规则自动格式化代码
javascript·ide·vscode
yw00yw4 小时前
常见的设计模式
开发语言·javascript·设计模式
百锦再4 小时前
WebSocket vs RabbitMQ:聊天室技术选型分析
websocket·网络协议·rabbitmq·消息·聊天室·messge
兮漫天5 小时前
bun + vite7 的结合,孕育的 Robot Admin 【靓仔出道】(二十)终章
前端·javascript·vue.js
poemyang5 小时前
RPC的三大问题:跨语言、跨平台通信的终极解决方案是如何炼成的?
网络协议·rpc·序列化