const { Server: ServerIO } = require('socket.io') const { io: ClientIO } = require('socket.io-client') const { readHostList } = require('../utils') const { clientPort } = require('../config') const { verifyAuthSync } = require('../utils') let clientSockets = {}, clientsData = {} async function getClientsInfo(socketId) { let hostList = await readHostList() hostList ?.map(({ host, name }) => { let clientSocket = ClientIO(`http://${ host }:${ clientPort }`, { path: '/client/os-info', forceNew: true, timeout: 5000, reconnectionDelay: 3000, reconnectionAttempts: 3 }) // 将与客户端连接的socket实例保存起来,web端断开时关闭这些连接 clientSockets[socketId].push(clientSocket) return { host, name, clientSocket } }) .map(({ host, name, clientSocket }) => { clientSocket .on('connect', () => { consola.success('client connect success:', host, name) clientSocket.on('client_data', (osData) => { clientsData[host] = osData }) clientSocket.on('client_error', (error) => { clientsData[host] = error }) }) .on('connect_error', (error) => { consola.error('client connect fail:', host, name, error.message) clientsData[host] = null }) .on('disconnect', () => { consola.info('client connect disconnect:', host, name) clientsData[host] = null }) }) } module.exports = (httpServer) => { const serverIo = new ServerIO(httpServer, { path: '/clients', cors: { origin: '*' // 需配置跨域 } }) serverIo.on('connection', (socket) => { // 前者兼容nginx反代, 后者兼容nodejs自身服务 let clientIp = socket.handshake.headers['x-forwarded-for'] || socket.handshake.address socket.on('init_clients_data', async ({ token }) => { // 校验登录态 const { code, msg } = await verifyAuthSync(token, clientIp) if (code !== 1) { socket.emit('token_verify_fail', msg || '鉴权失败') socket.disconnect() return } // 收集web端连接的id clientSockets[socket.id] = [] consola.info('client连接socketId: ', socket.id, 'clients-socket已连接数: ', Object.keys(clientSockets).length) // 获取客户端数据 getClientsInfo(socket.id) // 立即推送一次 socket.emit('clients_data', clientsData) // 向web端推送数据 let timer = null timer = setInterval(() => { socket.emit('clients_data', clientsData) }, 1000) // 关闭连接 socket.on('disconnect', () => { // 防止内存泄漏 if (timer) clearInterval(timer) // 当web端与服务端断开连接时, 服务端与每个客户端的socket也应该断开连接 clientSockets[socket.id].forEach(socket => socket.close && socket.close()) delete clientSockets[socket.id] consola.info('断开socketId: ', socket.id, 'clients-socket剩余连接数: ', Object.keys(clientSockets).length) }) }) }) }