import io from 'socket.io-client' import { setBaseOption, BaseOption } from './storage' import { type SocketOptions, SocketEvent } from './type' import { SocketStatus, HskTerminatedCode } from './outputType' import EventEmitter from '../eventemitter' import { Logger } from '../Logger' class HsSocket extends EventEmitter { private logger: Logger public socket?: typeof io.Socket /** 初始化 socket 需要的参数 */ public socketOptions: SocketOptions /** 心跳延迟时间 */ private heartBeatDelay: number /** 主动关闭链接时间 */ private closeHeartBeatDelay: number /** 心跳检测定时器 */ private heartBeatTimer: null | ReturnType /** 清空心跳检测定时器 */ private closeHeartBeatTimer: null | ReturnType /** 超时次数 */ // private timeOutCount: number /** 最大超时次数限制 */ private imRetryCount: number /** 本次 socket 会话唯一 id */ private sessionId: string constructor(socketOptions: SocketOptions) { super() /** 页面关闭时 关闭 socket */ window.addEventListener('onunload', this.closeSocket) this.logger = new Logger(socketOptions.loggerLevel, 'HsSocket') this.socket = undefined this.heartBeatTimer = null this.closeHeartBeatTimer = null // this.timeOutCount = 0 this.sessionId = '' this.socketOptions = socketOptions this.heartBeatDelay = socketOptions.imHeartTime * 1000 || 3000 this.closeHeartBeatDelay = socketOptions.imHeartTime * 1000 || 3000 this.imRetryCount = socketOptions.imRetryCount || 10 } /** @public initSocket 初始化 Socket 连接 */ public initSocket(): void { /** 如果有未断开的连接先断开 */ this.closeSocket() // 设置状态为连接中 this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Connecting }) /** https://socket.io/docs/v2/client-api/#iourl */ this.socket = io(this.socketOptions.imWsServer, { transports: ['websocket'], reconnectionAttempts: this.imRetryCount, reconnectionDelay: this.heartBeatDelay, reconnectionDelayMax: this.heartBeatDelay + 1000, timeout: this.heartBeatDelay, reconnection: true }) /** https://socket.io/docs/v2/client-api/#event-error */ this.socket.on('error', (error: object) => { this.logger.error(`socket_error | ${JSON.stringify(error)}`) }) /** https://socket.io/docs/v2/client-api/#event-connect_error-1 */ this.socket.on('connect_error', (error: object) => { const errorData = `socket_connect_error | ${JSON.stringify(error)}` this.logger.warn(errorData) }) this.socket.on('reconnecting', (res: string) => { this.logger.error(`socket_warn | socket_reconnecting | ${res}`) this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.ReTry }) }) this.socket.on('reconnect', (res: string) => { this.logger.error(`socket_warn | socket_reconnect | ${res}`) this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Ready }) }) this.socket.on('reconnect_failed', (error: string) => { this.logger.error(`socket_warn | socket_reconnect_failed | ${error}`) this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Terminated, code: HskTerminatedCode.SocketOnReconnectFailed, error: `${error}` }) }) /** https://socket.io/docs/v2/client-api/#event-connect */ this.socket.on('connect', () => { this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Connected }) this.socketLogin() }) /** https://socket.io/docs/v2/client-api/#event-disconnect */ this.socket.on('disconnect', (reason: string) => { const errorMessage = `socket_disconnect | ${reason}` this.logger.warn(errorMessage) }) /** 服务端下行事件 */ this.socket.on('common_down_data', (e: string) => { // console.log(e, 3434343434) if (e && JSON.parse(e) && JSON.parse(e).data) { this.emit(SocketEvent.SocketDownEvent, { eventData: JSON.parse(JSON.parse(e).data) }) } }) /** 服务端下行指令 */ this.socket.on('common_down_cmd', (e: string) => { const { clientSessionId } = JSON.parse(e) // console.log('dsdsdsdsdsds', clientSessionId) if (clientSessionId === this.sessionId) { this.logger.error( `socket status | ${SocketStatus.Terminated} | 坐席在其他页面重新初始化,本页面被踢出` ) this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Terminated, code: HskTerminatedCode.SocketRepeatLogin, error: '您已在其他页面签入,当前页面连接已断开' }) } }) } /** @private socketLogin 客户端上行登录事件 */ private socketLogin() { const data = { // appCode: this.socketOptions.appCode || '1111', // token: this.socketOptions.token || '1111', userId: this.socketOptions.agent_id } this.socket && this.socket.emit('login', data, (sessionId: string) => { // console.log(sessionId, '测试一下') this.emit(SocketEvent.SetSocketStatus, { status: SocketStatus.Ready }) setBaseOption(BaseOption.TrackParams, { socket_session_id: sessionId }) this.sessionId = sessionId this.startHeartbeat() }) } /** @public closeSocket 关闭 socket 连接 */ public closeSocket() { if (this.socket) { this.socket.io.opts.reconnection = false this.socket.close() // 清除之前的监听事件 this.socket.removeAllListeners() } this.socket = undefined this.sessionId = '' if (this.heartBeatTimer) { window.clearTimeout(this.heartBeatTimer) this.heartBeatTimer = null } if (this.closeHeartBeatTimer) { window.clearTimeout(this.closeHeartBeatTimer) this.closeHeartBeatTimer = null } } /** @private startHeartbeat 开启心跳检测 */ private startHeartbeat() { if (this.heartBeatTimer) { window.clearTimeout(this.heartBeatTimer) this.heartBeatTimer = null } if (this.closeHeartBeatTimer) { window.clearTimeout(this.closeHeartBeatTimer) this.closeHeartBeatTimer = null } this.socket && this.heartbeatEvent() } /** @private heartbeatEvent websocket心跳检测 */ private heartbeatEvent() { this.heartBeatTimer = setTimeout(() => { this.socket && this.sendHeartbeat() /** 如果心跳检测一直没回应,则进行重连 */ this.closeHeartBeatTimer = setTimeout(() => { this.logger.warn('socket_heart_beat | 心跳超时,即将重新连接') this.initSocket() }, this.closeHeartBeatDelay) }, this.heartBeatDelay) } /** @private sendHeartbeat 客户端上行心跳事件 */ private sendHeartbeat() { this.socket && this.socket.emit( 'heartbeat', JSON.stringify({ ...this.socketOptions }), () => { this.startHeartbeat() } ) } } export default HsSocket