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: 'basesocket: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: 'basesocket:onMessage', _type: 'CLOG', usid: socket.usid, skevent: dataObj?.skevent }, '\n,') } catch (exception) { console.error({ _at: new Date().toJSON(), _from: 'basesocket: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: 'basesocket: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: 'basesocket: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) { try { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } catch (expt) { console.error( { _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) { // 20240916 注意到,前端显示仍然连接着 socket,但后台却判断为不是 OPEN。所以把 if 改成 try. try { socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj) } catch (expt) { console.error( { _at: new Date().toJSON(), _from: 'Socket:sendToOne', _type: 'CWARN', msg: 'sendToOne: Failed sending to unconnected socket', dataObj, usid }, '\n,' ) delete my.socketPool[usid] } }, }