const ws = require('ws') const webtoken = require('basend-webtoken') const my = { wssServer: undefined, socketPool: {}, listeners: {}, } module.exports = { initSocket (webServer) { my.wssServer = new ws.Server({ server: webServer }) console.info('App Socket Server attached to web server.') my.wssServer.on('connection', (socket, req) => { console.info(`A socket from App Client is connected from ${req.connection.remoteAddress}:${req.connection.remotePort}.`) // socket.isAlive = true // socket.on('pong', function() { console.log('👈 ASS: on Pong'); this.isAlive = true }) socket.on('message', (data) => { // 在这里统一分发消息 console.log('App Socket Client message: ', data) let dataObj try { dataObj = JSON.parse(data) } catch (exception) { console.log(new Date().toJSON(), 'Unable to parse socket message: ', data) return } if (dataObj.skevent === 'SOCKET_OWNER') { dataObj._passtokenSource = webtoken.verifyToken(dataObj._passtoken) // todo: 为防止前端欺骗,应当用和login里类似的方法来检查来检查 if (typeof dataObj._passtokenSource?.uuid === 'string') { my.socketPool[dataObj._passtokenSource.uuid] = socket console.log('收到Login 成功的消息,绑定socket', Object.keys(my.socketPool)) } } const listeners = my.listeners[dataObj.skevent] || [] for (const listener of listeners) { listener(dataObj) } }) }) return this }, removeUserSocket (uuid) { delete my.socketPool[uuid] }, 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.wssServer.clients.forEach((socket) => { if (socket.readyState === socket.OPEN) { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } else { delete my.socketPool[socket.uuid] } }) }, sendToOne (dataObj, uuid) { const socket = my.socketPool[uuid] if (socket && socket.readyState === socket.OPEN) { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } else { delete my.socketPool[uuid] } }, } // todo: 前端断线重连时,并不会再次 login_success。也许在前端的initSocket时,应当把_passtoken送过来,而后台则对_passtoken做验证后再加socketPool。