ball_admin/src/util/socket.ts

145 lines
4.1 KiB
TypeScript

import Config from "./config"
export type AutoReconnectOptions = boolean | {
maxRetries?: number
retryInterval?: number
onMaxRetriesReached?: Function
}
export enum ConnectionStatus {
Disconnected = 'DISCONNECTED',
Connected = 'CONNECTED',
Error = 'ERROR'
}
class SocketService {
private static instance: SocketService | null = null
private ws: WebSocket | null = null
private listeners: Record<string, Function[]> = {}
private autoReconnect: AutoReconnectOptions = true
private times: any = null
private retries: number = 0
private connectionStatus: ConnectionStatus = ConnectionStatus.Disconnected
private constructor() {
this.connect()
}
public static getInstance(): SocketService {
if (!SocketService.instance) {
SocketService.instance = new SocketService()
}
return SocketService.instance
}
public setAutoReconnectOptions(options: AutoReconnectOptions) {
this.autoReconnect = options
}
public connect() {
this.ws = new WebSocket(Config.ws)
this.ws.onopen = () => {
this.connectionStatus = ConnectionStatus.Connected
this.emit('connected', null)
this.hert()
}
this.ws.onerror = () => {
this.connectionStatus = ConnectionStatus.Error
clearTimeout(this.times)
this.emit('error', null)
}
this.ws.onclose = () => {
this.connectionStatus = ConnectionStatus.Disconnected
clearTimeout(this.times)
this.emit('disconnected', null)
if (this.shouldReconnect()) {
setTimeout(() => this.connect(), this.getRetryInterval())
}
}
this.ws.onmessage = (event) => {
this.emit('message', event.data)
}
}
public hert(){
this.times = setTimeout(() => this.send({"type":"heartbeat"}), 3000)
}
private shouldReconnect(): boolean {
if (typeof this.autoReconnect === 'boolean') {
return this.autoReconnect
} else if (this.autoReconnect) {
const { maxRetries } = this.autoReconnect
if (maxRetries !== undefined) {
if (this.retries < maxRetries) {
this.retries++
return true
} else if (this.retries >= maxRetries) {
this.autoReconnect.onMaxRetriesReached && this.autoReconnect.onMaxRetriesReached()
return false
}
}
}
return false
}
private getRetryInterval(): number {
if (typeof this.autoReconnect === 'boolean') {
return 1000
} else if (this.autoReconnect && this.autoReconnect.retryInterval) {
return this.autoReconnect.retryInterval
}
return 1000
}
public send(data: any) {
if (this.ws && this.ws.readyState === WebSocket.OPEN) {
this.ws.send(JSON.stringify(data))
} else {
console.error('WebSocket 连接未打开')
}
}
public close() {
if (this.ws) {
this.ws.close()
SocketService.instance = null
}
}
private emit(event: string, data: any) {
if (!this.listeners[event]) {
return
}
this.listeners[event].forEach((listener) => listener(data))
}
public on(event: string, listener: Function) {
if (!this.listeners[event]) {
this.listeners[event] = []
}
this.listeners[event].push(listener)
if (event === 'connected' && this.connectionStatus === ConnectionStatus.Connected) {
listener()
}
}
public off(event: string, listener: Function) {
if (!this.listeners[event]) {
return
}
this.listeners[event] = this.listeners[event].filter((l) => l !== listener)
}
public getConnectionStatus(): ConnectionStatus {
return this.connectionStatus
}
public watchConnectionStatus(callback: (status: ConnectionStatus) => void) {
this.on('connected', () => callback(ConnectionStatus.Connected))
this.on('disconnected', () => callback(ConnectionStatus.Disconnected))
this.on('error', () => callback(ConnectionStatus.Error))
}
}
export default SocketService