mm_os
Version:
MM_OS服务端架构,用于快速构建应用程序,支持网站建设、小程序后台、AI应用、物联网(IOT/AIOT)、游戏服务端等多种场景。
431 lines (387 loc) • 11.2 kB
JavaScript
/**
* 适配器基类
* 定义了基础的网络通信接口
*/
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;