const ws = require('ws') const webtoken = require('wo-base-webtoken') const my = { wsServer: undefined, socketPool: {}, listeners: {}, } module.exports = { initSocket (webServer) { my.wsServer = new ws.Server({ server: webServer }) console.info({ _at: new Date().toJSON(), _from: 'Socket:initSocket', _type: 'CLOG', about: 'Base Socket Server is initialized.' }, '\n,') my.wsServer.on('connection', (socket, req) => { //console.info({_at:new Date().toJSON(), _from: 'Socket:onConnection', _type:'CLOG', about: `A socket is connecting from ${req.connection.remoteAddress}:${req.connection.remotePort}.`},'\n,') // socket.isAlive = true // socket.on('pong', function() { this.isAlive = true }) socket.on('message', (data) => { // 在这里统一分发消息 let dataObj try { dataObj = JSON.parse(data) if (dataObj?.skevent === 'PING') { return } console.log({ _at: new Date().toJSON(), _from: 'Socket:onMessage', _type: 'CLOG', usid: socket.usid, dataObj }, '\n,') } catch (exception) { console.error({ _at: new Date().toJSON(), _from: 'Socket:onMessage', _type: 'CERROR', about: 'Unable to parse socket message', data }, '\n,') return } if (['SOCKET_OWNER', 'SOCKET_OWNER_RECONNECT'].includes(dataObj.skevent)) { // 前端断线重连时,并不会自动再次提供 _passtoken。在前端的initSocket时,应当把_passtoken送过来,而后台则对_passtoken做验证后再加socketPool。 dataObj._passtokenSource = webtoken.verifyToken(dataObj._passtoken) if (typeof dataObj._passtokenSource?.usid === 'string') { my.socketPool[dataObj._passtokenSource.usid] = socket socket.usid = dataObj._passtokenSource.usid console.log({ _at: new Date().toJSON(), _from: 'Socket:onMessage', _type: 'CLOG', skevent: dataObj.skevent, usid: dataObj._passtokenSource.usid, socketPool: Object.keys(my.socketPool)?.length, socketClients: my.wsServer.clients.size }, '\n,') } } const listeners = my.listeners[dataObj.skevent] || [] for (const listener of listeners) { listener(dataObj) } }) // const heartbeat = setInterval(() => { // my.wsServer.clients.forEach((socket) => { // if (socket.isAlive === false) return socket.terminate() // socket.isAlive = false // socket.ping(function() { wo.cclog('👉 ASS: sent Ping') }) // }) // }, 60000) socket.on('close', () => { //console.log({_at:new Date().toJSON(), _from: 'Socket:onClose', _type:'CLOG', usid: socket?.usid},'\n,') // don't know why, but this output happens too often without usid. delete my.socketPool[socket?.usid] // clearInterval(heartbeat) }) }) return this }, removeUserSocket (usid) { delete my.socketPool[usid] }, addListener (skevent, listener) { if (Array.isArray(my.listeners[skevent]) && typeof listener === 'function') { my.listeners[skevent].push(listener) } else { my.listeners[skevent] = [listener] } return this }, sendToAll (dataObj) { my.wsServer.clients.forEach((socket) => { if (socket.readyState === socket.OPEN) { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } else { console.warn({ _at: new Date().toJSON(), _from: 'Socket:sendToAll', _type: 'CWARN', msg: 'sendToAll: Failed sending to unconnected socket', dataObj, usid: socket.usid }, '\n,') delete my.socketPool[socket.usid] } }) }, sendToOne (dataObj, usid) { const socket = my.socketPool[usid] if (socket && socket.readyState === socket.OPEN) { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } else { console.warn({ _at: new Date().toJSON(), _from: 'Socket:sendToOne', _type: 'CWARN', msg: 'sendToOne: Failed sending to unconnected socket', dataObj, usid }, '\n,') delete my.socketPool[usid] } }, }