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(new Date().toJSON(), '[LOG] Base Socket Server is initialized.') my.wsServer.on('connection', (socket, req) => { //console.info(new Date().toJSON(), `[LOG] A socket is connecting from ${req.connection.remoteAddress}:${req.connection.remotePort}.`) // socket.isAlive = true // socket.on('pong', function() { this.isAlive = true }) socket.on('message', (data) => { // 在这里统一分发消息 let dataObj try { dataObj = JSON.parse(data) console.log(new Date().toJSON(), '[LOG] 收到 App Socket Event: ', dataObj?.skevent) } catch (exception) { console.log(new Date().toJSON(), '[LOG] Unable to parse socket message: ', data) return } if (['SOCKET_OWNER', 'SOCKET_OWNER_RECONNECT'].includes(dataObj.skevent)) { 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( new Date().toJSON(), '[LOG]', dataObj.skevent === 'SOCKET_OWNER' ? 'Login' : 'Reconnect', '绑定 socket 到', dataObj._passtokenSource.usid, 'socketPool.length =', Object.keys(my.socketPool)?.length, ', socket clients size =', my.wsServer.clients.size ) } } 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(new Date().toJSON(), '[LOG] Closing socket of usid =', socket?.usid) // 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 { 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 { delete my.socketPool[usid] } }, } // todo: 前端断线重连时,并不会再次 login_success。也许在前端的initSocket时,应当把_passtoken送过来,而后台则对_passtoken做验证后再加socketPool。