import { WsBaseUrl } from '@/config/env'
import { StorageType, WebsocketEventType } from '@/types'
import { DeviceEventEmitter } from 'react-native'
import { getItem } from './storage'
import { generateUUID } from '.'
import { autoResendFailRequest } from './failRequest'
import { useUserStore } from '@/store'
interface WebSocketManagerOptions {
url: string
protocols?: Array<string>
reconnectTimeout?: number
heartbeatTimeout?: number
pongTimeout?: number
}
class WebSocketManager {
private static instance: WebSocketManager | null = null
private ws: WebSocket | null = null
private heartbeatTimer: NodeJS.Timeout | null = null
private reconnectTimer: NodeJS.Timeout | null = null
private isReconnecting: boolean = false
public message: string = 'ping'
public url: string
public protocols: Array<string> = []
public reconnectTimeout: number
public heartbeatTimeout: number
public pongTimeout: number
private constructor(options: WebSocketManagerOptions) {
this.url = options.url
this.protocols = options.protocols ?? []
this.reconnectTimeout = options.reconnectTimeout ?? 15e3
this.heartbeatTimeout = options.heartbeatTimeout ?? 15e3
this.pongTimeout = options.pongTimeout ?? 1e4
}
public static getInstance(options: WebSocketManagerOptions): WebSocketManager {
if (!WebSocketManager.instance) {
WebSocketManager.instance = new WebSocketManager({ ...options })
console.log('新增的websocket实例')
return WebSocketManager.instance
}
console.log('共用同一个websocket实例')
return WebSocketManager.instance
}
connect() {
if (this.ws) {
this.ws.close(1000)
}
console.log('建立了连接', this.url, this.protocols)
this.ws = new WebSocket(this.url, this.protocols)
this.ws.onopen = () => {
console.log('WebSocket连接已建立')
DeviceEventEmitter.emit(WebsocketEventType.OPEN)
this.startHeartbeat()
}
this.ws.onmessage = (message) => {
if (message.data === 'pong') {
this.resetHeartbeat()
if (!global.isErrorRepeat) {
global.betchUpdateEventTracking()
}
autoResendFailRequest()
} else if (message.data === 'close') {
console.log(message.data, 'message.data')
this.close(1000)
return false
} else {
console.log('发送来的消息', typeof message.data, message.data)
const data = message?.data ? JSON.parse(message.data) : {}
DeviceEventEmitter.emit(WebsocketEventType.MESSAGE, data)
}
}
this.ws.onerror = (error) => {
console.error('WebSocket错误:', error)
DeviceEventEmitter.emit(WebsocketEventType.ERROR, error)
}
this.ws.onclose = (event) => {
console.log('WebSocket已关闭', event)
DeviceEventEmitter.emit(WebsocketEventType.CLOSE, event)
if (event.code !== 1000) {
this.handleReconnect()
global.betchUpdateEventTracking({ isForce: true })
}
}
}
send(data) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(data)
} else {
console.error('WebSocket连接未打开')
}
}
startHeartbeat() {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(this.message)
this.heartbeatTimer = setTimeout(() => {
console.error('没有收到 pong 响应,关闭连接')
this.ws?.close(1000)
}, this.pongTimeout)
}
}
private resetHeartbeat(): void {
if (this.heartbeatTimer) {
clearTimeout(this.heartbeatTimer)
}
setTimeout(() => {
this.startHeartbeat()
}, this.heartbeatTimeout)
}
handleReconnect() {
if (this.isReconnecting) return
this.isReconnecting = true
this.reconnectTimer = setTimeout(() => {
console.log('尝试重连 WebSocket')
this.connect()
this.isReconnecting = false
}, this.reconnectTimeout)
}
close(code?: number) {
if (this.ws) {
this.ws.close(code)
WebSocketManager.instance = null
}
if (!this.ws && code === 1000) {
WebSocketManager.instance = null
}
if (this.heartbeatTimer) {
clearTimeout(this.heartbeatTimer)
}
if (this.reconnectTimer) {
clearTimeout(this.reconnectTimer)
}
}
}
const webSocketManager = () => {
const { token, userInfo, allAppMenu, appMenu } = useUserStore.getState()
const ID = (userInfo.ID || userInfo.id) ?? ''
const uuid = generateUUID()
if (!ID || !token) return
const protocols = [token, '1', String(ID), uuid, 'json']
console.log('protocols', protocols)
return WebSocketManager.getInstance({
url: `${WsBaseUrl}/sysSettingMessages/getWebSocket`,
protocols,
})
}
export default webSocketManager