const my = { socket: undefined, reconnecting: undefined, heartbeating: undefined, listeners: {}, heartbeatInterval: 20000, reconnectInterval: 5000, messageQueue: [], } // uni.onSocket* 和 sendSocketMessage/closeSocket 方法,是全局唯一的 api,不需要保存 socket 对象。 // SocketTask 的 on* 和 send/close 方法,是针对具体 socket 的,适用于多个连接(但不一定支持)或者需要对唯一连接做更精细控制。 export default { isAlive () { return my.socket && my.socket.readyState === my.socket.OPEN }, initSocket ({ url, relogin = false, stateManager = {}, heartbeat = false, heartbeatInterval, reconnectInterval } = {}) { if (!my.socket || (my.socket.readyState !== my.socket.OPEN && typeof url === 'string')) { console.log({ _at: new Date().toJSON(), about: `WebSocket is connecting to ${url}...` }) my.socket = uni.connectSocket({ url: url.replace(/^http/, 'ws'), complete: () => {}, }) my.socket.onOpen((res) => { console.log({ _at: new Date().toJSON(), about: 'WebSocket onOpen: ', res }) stateManager.socketAlive = true if (my.messageQueue.length) { console.log('WebSocket_onOpen: sending messageQueue') my.messageQueue.forEach((dataObj) => { this.sendObject(dataObj) }) my.messageQueue = [] } clearInterval(my.reconnecting) delete my.reconnecting // 前端断线重连时,并不会自动提供 _passtoken,应当把_passtoken送给后台,而后台则对_passtoken做验证后再加socketPool。 if (relogin && uni.getStorageSync('_passtoken')) { console.log({ _at: new Date().toJSON(), about: 'Reporting owner for reconnecting socket' }) my.socket.send({ data: JSON.stringify({ skevent: 'SOCKET_OWNER_RECONNECT', _passtoken: uni.getStorageSync('_passtoken') }) }) } if (heartbeat) { my.heartbeating = setInterval(() => { if (my.socket && my.socket.readyState === my.socket.OPEN) { my.socket.send({ data: JSON.stringify({ skevent: 'PING' }) }) } else { clearInterval(my.heartbeating) delete my.heartbeating } }, heartbeatInterval || my.heartbeatInterval) // 定期发送心跳,避免被关闭 } }) my.socket.onClose((res) => { console.log({ _at: new Date().toJSON(), about: 'Websocket onClose: ', res }) stateManager.socketAlive = false if (!my.reconnecting) my.reconnecting = setInterval(() => { console.log({ _at: new Date().toJSON(), about: 'Websocket reconnecting...' }) this.initSocket({ url, relogin: true, stateManager }) }, reconnectInterval || my.reconnectInterval) // 定时尝试重连 }) my.socket.onError((err) => { console.log({ _at: new Date().toJSON(), about: 'Websocket onError: ', err }) stateManager.socketAlive = false }) my.socket.onMessage(({ data }) => { // 在这里统一分发消息(用户端通常不需要返回结果给服务器,因此不用 rpc 模式,而用 event 模式。 try { let { skevent, ...apiWhat } = JSON.parse(data) console.log({ _at: new Date().toJSON(), about: 'Websocket onMessage', skevent, apiWhat }) let listeners = my.listeners[skevent] || [] for (let listener of listeners) { listener(apiWhat) } } catch (exception) { console.log({ _at: new Date().toJSON(), about: 'Websocket onMessage unknown', data, exception }) return } }) } return this }, closeSocket () { if (my.socket) my.socket.close() setTimeout(() => { clearInterval(my.reconnecting) delete my.reconnecting }, 2000) }, initListener (skevent, listener) { // 当该 skevent 尚不具有任何 listener 时,添加本 listener my.listeners[skevent]?.length > 0 || this.addListener(skevent, listener) return this }, addListener (skevent, listener) { if (Array.isArray(my.listeners[skevent]) && typeof listener === 'function') { my.listeners[skevent].push(listener) } else { my.listeners[skevent] = [listener] } return this }, countListener (skevent) { if (Array.isArray(my.listeners[skevent])) { return my.listeners[skevent].length } return 0 }, sendObject (dataObj = {}) { console.log('sendObject! socket.readyState =', my.socket.readyState) // 把 sendObject({_passtoken}) 从其他零散地方迁移到这里来 if (!dataObj._passtoken) { dataObj._passtoken = uni.getStorageSync('_passtoken') || undefined } if (my.socket && my.socket.readyState === my.socket.OPEN) { my.socket.send({ data: typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj, }) } else { my.messageQueue.push(dataObj) } }, }