108 lines
4.1 KiB
JavaScript
108 lines
4.1 KiB
JavaScript
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.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) {
|
||
socket.send(typeof dataObj !== 'string' ? JSON.stringify(dataObj) : dataObj)
|
||
} else {
|
||
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]
|
||
}
|
||
},
|
||
}
|