UNPKG

mm_os

Version:

MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。

431 lines (387 loc) 11.2 kB
/** * 适配器基类 * 定义了基础的网络通信接口 */ const { Mod } = require('mm_machine'); const { idGen } = require('../ulits/id_gen.js'); class Adapter extends Mod { static config = { name: 'adapter', host: '0.0.0.0', port: 8000 }; /** * 构造函数 * @param {object} config 配置参数 * @param {object} parent 父对象 */ constructor(config, parent) { super({ ...Adapter.config, ...config }, parent); // 客户端连接列表 this.clients = new Map(); // 中间件数组 this.mw_list = []; // 回调函数映射表,使用消息ID作为键 this.callback = new Map(); } } /** * 生成ID * @param {string} type ID类型,可选值:'client'(客户端ID)或'msg'(消息ID) * @returns {string} 唯一ID */ Adapter.prototype.genId = function (type = 'client') { return idGen.genId(type); }; /** * 生成客户端ID * @returns {string} 唯一客户端ID */ Adapter.prototype.genClientId = function () { return this.genId('client'); }; /** * 生成消息ID * @returns {string} 唯一消息ID */ Adapter.prototype.genMsgId = function () { return this.genId('msg'); }; /** * 使用中间件 * @param {Function} middleware 中间件函数,格式: async (ctx, next) => {} * @returns {object} this */ Adapter.prototype.use = function (middleware) { if (typeof middleware !== 'function') { throw new Error('中间件必须是一个函数'); } this.mw_list.push(middleware); return this; // 支持链式调用 }; /** * 设置中间件(子类可以重写以提供特定实现) */ Adapter.prototype._setupMw = function () { // 默认中间件设置,子类可以重写 }; /** * 发送消息(需要子类实现) * @param {string} client_id 客户端ID * @param {string} message 消息内容 * @returns {void} */ Adapter.prototype._send = function (client_id, message) { this.log('debug', '发送消息', { client_id, message }); throw new Error('子类必须实现_send方法'); }; /** * 向指定客户端发送消息(JSON 2.0格式) * @param {string} client_id 客户端ID * @param {string} method 方法名 * @param {object} params 参数对象 * @param {Function} cb 可选,接收客户端回复的回调函数 * @returns {void} */ Adapter.prototype.send = function (client_id, method, params = {}, cb = null) { // 生成唯一消息ID let message_id = this.genMsgId(); // 构建JSON 2.0格式的消息 let msg = { id: message_id, method: method, params: params }; // 转换为字符串 let message = JSON.stringify(msg); // 如果提供了回调函数,保存起来等待客户端回复 if (typeof cb === 'function') { this.callback.set(message_id, cb); } return this._send(client_id, message); }; /** * 异步发送消息 * @param {string} client_id 客户端ID * @param {string} method 方法名 * @param {object} params 参数对象 * @returns {Promise} 异步操作的结果 */ Adapter.prototype.sendAsync = function (client_id, method, params = {}) { return new Promise((resolve, reject) => { // 发送消息 this.send(client_id, method, params, (msg, error) => { if (error) { reject(error); } else { resolve(msg); } }); }); }; /** * 组合中间件(洋葱模型) * @param {object} ctx 上下文对象 */ Adapter.prototype._composeMw = async function (ctx) { let index = -1; let dispatch = async (i) => { if (i <= index) { throw new Error('next() 不能被多次调用'); } index = i; let middleware = this.mw_list[i]; if (!middleware) { return Promise.resolve(); } try { return await middleware(ctx, () => dispatch(i + 1)); } catch (error) { this.log('error', '中间件执行错误:', error); throw error; } }; try { await dispatch(0); this._autoReply(ctx); } catch (error) { this._runMwError(ctx, error); } }; /** * 自动回复客户端 * @param {object} ctx 上下文对象 * @private */ Adapter.prototype._autoReply = function (ctx) { if (ctx.body === undefined || ctx.body === null) return; // 检查原始消息是否是JSON 2.0格式 if (typeof ctx.message === 'object' && ctx.message.id) { // 构建JSON 2.0格式的回复 let response = { id: ctx.message.id, result: ctx.body, error: null }; this._send(ctx.client_id, JSON.stringify(response)); } else { // 非JSON格式消息,直接发送 this._send(ctx.client_id, ctx.body.toString()); } }; /** * 处理中间件错误 * @param {object} ctx 上下文对象 * @param {Error} error 错误对象 * @private */ Adapter.prototype._runMwError = function (ctx, error) { this.log('error', '中间件链执行错误:', error); // 如果是JSON 2.0格式的请求,发送错误回复 if (typeof ctx.message === 'object' && ctx.message.id) { let response = { id: ctx.message.id, result: null, error: { message: error.message } }; this._send(ctx.client_id, JSON.stringify(response)); } }; /** * 消息接收处理 * @param {string} client_id 客户端ID * @param {object} socket 客户端socket对象 * @param {string} data 接收的消息 */ Adapter.prototype._onMsg = function (client_id, socket, data) { // 检查参数是否有效 if (!this._check(client_id, data)) return; // 处理消息数据 let client_id_str = this._getClientIdStr(client_id); let message = this._parseStr(data); this.log('debug', `客户端 ${client_id_str} 发送数据: ${message}`); // 解析消息并处理 let msg = this._parseMessage(client_id_str, message); // 创建上下文对象并执行中间件 let ctx = this._createContext(client_id_str, socket, msg); this._composeMw(ctx); }; /** * 验证参数有效性 * @param {string} client_id 客户端ID * @param {string} data 消息数据 * @returns {boolean} 参数是否有效 * @private */ Adapter.prototype._check = function (client_id, data) { if (!client_id) { this.log('error', '客户端ID无效:', client_id); return false; } if (!data) { this.log('error', '消息数据无效:', data); return false; } return true; }; /** * 获取客户端ID字符串 * @param {string|object} client_id 客户端ID * @returns {string} 客户端ID字符串 * @private */ Adapter.prototype._getClientIdStr = function (client_id) { return typeof client_id === 'object' ? client_id.id || 'unknown' : client_id; }; /** * 解析数据为字符串 * @param {any} data 原始数据 * @returns {string} 字符串消息 * @private */ Adapter.prototype._parseStr = function (data) { if (Buffer.isBuffer(data)) { return data.toString(); } else if (typeof data === 'string') { return data; } else { return String(data); } }; /** * 解析消息内容 * @param {string} client_id_str 客户端ID字符串 * @param {string} message 消息内容 * @returns {any} 解析后的消息 * @private */ Adapter.prototype._parseMessage = function (client_id_str, message) { try { let msg = JSON.parse(message); // 检查是否是回复消息(具有id属性) if (typeof msg === 'object' && msg.id) { this._onMessage(client_id_str, msg); } return msg; } catch (error) { this.log('warn', `客户端 ${client_id_str} 发送的消息不是有效的JSON格式: ${message}`, error); // 如果不是JSON格式,仍传递给中间件处理 return message; } }; /** * 创建上下文对象 * @param {string} client_id_str 客户端ID字符串 * @param {object} socket 客户端socket对象 * @param {any} msg 消息内容 * @returns {object} 上下文对象 * @private */ Adapter.prototype._createContext = function (client_id_str, socket, msg) { return { client_id: client_id_str, socket: socket, message: msg, adapter: this, response: '', body: null, // 发送消息给当前客户端 send: (method, params, cb) => { this.send(client_id_str, method, params, cb); }, // 异步发送消息 sendAsync: (method, params = {}) => { return this.sendAsync(client_id_str, method, params); } }; }; /** * 消息接收处理 * @param {string} client_id 客户端ID * @param {object} msg 接收的消息 */ Adapter.prototype._onMessage = function (client_id, msg) { // 查找对应的回调函数 this.log('debug', '收到消息', { client_id }); let callback = this.callback.get(msg.id); if (callback) { // 执行回调函数并移除 this.callback.delete(msg.id); // 回调参数:消息对象,错误对象(如果有) let error = msg.error ? new Error(msg.error.message) : null; callback(msg, error); return; } }; /** * 连接关闭处理 * @param {string} client_id 客户端ID */ Adapter.prototype._onClose = function (client_id) { // 移除客户端连接 this.clients.delete(client_id); this.log('info', `客户端断开连接: ${client_id}`); }; /** * 错误处理 * @param {string} client_id 客户端ID * @param {Error} error 错误对象 */ Adapter.prototype._onError = function (client_id, error) { this.log('error', `客户端 ${client_id} 发生错误:`, error); // 移除客户端连接 this.clients.delete(client_id); }; /** * 停止Adapter服务器(子类可以重写以提供特定实现) */ Adapter.prototype._stop = function () { // 关闭所有客户端连接 for (let [id, client] of this.clients) { try { if (client.destroy) { // Socket客户端使用destroy方法 client.destroy(); } else if (client.close) { // WebSocket客户端使用close方法 client.close(); } } catch (error) { this.log('error', `关闭客户端 ${id} 连接时发生错误:`, error); } } }; /** * 停止Adapter服务器 */ Adapter.prototype.stop = function () { this._stop(); this.clients.clear(); // 关闭服务器 if (this.server) { this.server.close(() => { this.log('info', '服务器已停止'); }); } }; /** * 广播消息 * @param {string} message 消息内容 * @param {Array<string>} ids 可选,客户端ID数组,为空则向所有客户端广播 */ Adapter.prototype.bcast = function (message, ids = null) { if (ids && Array.isArray(ids)) { // 向指定客户端数组广播 for (let id of ids) { this._send(id, message); } this.log('debug', `服务器向指定客户端广播消息: ${message}`); } else { // 向所有客户端广播 for (let [id] of this.clients) { this._send(id, message); } this.log('debug', `服务器广播消息: ${message}`); } }; exports.Adapter = Adapter;