GreenDam

在飞牛OS部署MCP服务拉取系统日志

前言

最近 AI 再次成为热点,而 MCP 的出现在一定程度上弥补了大模型与外部系统交互能力的不足。
借助 MCP,模型不再只是生成文本,而是可以直接调用外部服务,从而真正参与到实际系统的操作与管理中。

本文通过一个简单示例,演示如何部署一个 MCP 服务,并为 AI 提供获取飞牛系统日志的能力。

服务代码

被折叠的代码 ```python #!/usr/bin/env python3 # -*- coding: utf-8 -*- """ trimrpc MCP Server — fnOS 系统管理 MCP 工具服务 ================================================ 通过 Streamable HTTP 传输提供 fnOS 系统管理能力的 MCP 工具服务器。 内置 trimrpc 二进制协议栈,通过 Unix Domain Socket 与本机 fnOS RPC 服务通信。零外部依赖,仅使用 Python 标准库。 可用工具: get_system_logs 获取系统事件日志 (登录、配置变更、SSH 等) trimrpc_call 通用 trimrpc RPC 调用 (任意服务/方法) discover_services 发现可用的 RPC 服务 用法: python3 report-trimrpc-mcp-202603062124.py # 默认 0.0.0.0:9800 python3 report-trimrpc-mcp-202603062124.py --port 8080 # 自定义端口 python3 report-trimrpc-mcp-202603062124.py --host 127.0.0.1 # 仅本机 python3 report-trimrpc-mcp-202603062124.py --list-tools # 列出工具 python3 report-trimrpc-mcp-202603062124.py -v # 调试日志 MCP 客户端配置 (Claude Code settings / .mcp.json): { "mcpServers": { "fnos": { "url": "http://:9800/mcp" } } } """ import sys import json import struct import socket import os import time import uuid import threading import logging from http.server import HTTPServer, BaseHTTPRequestHandler from socketserver import ThreadingMixIn # ═══════════════════════════════════════════════════════════════════ # 日志 # ═══════════════════════════════════════════════════════════════════ logging.basicConfig( stream=sys.stderr, level=logging.INFO, format='[trimrpc-mcp] %(levelname)s %(message)s', ) log = logging.getLogger('trimrpc-mcp') # ═══════════════════════════════════════════════════════════════════ # trimrpc 二进制协议核心 # # 协议格式: 30 字节固定头部 + 4 段变长数据 # 传输层: Unix Domain Socket (AF_UNIX, SOCK_STREAM) # ═══════════════════════════════════════════════════════════════════ CPRT_MAGIC = 0x54525043 # "CPRT" little-endian PROTOCOL_VERSION = 1 HEADER_SIZE = 30 BROKER_SERVICE = "com.trim.rpcbroker" BROKER_UDS_PATHS = [ "/run/trim_app_cgi/rpcbroker", ] DEFAULT_TIMEOUT = 10 SERVICE_PREFIX = "com." MAX_SERVICE_NAME_LEN = 128 _sid_counter = 0 _sid_lock = threading.Lock() def _next_session_id(): global _sid_counter with _sid_lock: _sid_counter += 1 return _sid_counter class RpcPacket: """ trimrpc 二进制数据包。 头部 (30 字节, little-endian): magic(4) + version(2) + session_id(8) + data_len(2) + token_len(2) + extra_data_len(4) + reserved(4) + extra_data2_len(4) 数据段: [data] + [token] + [extra_data(JSON 载荷)] + [extra_data2] """ HEADER_FMT = '<IHQHHIII' def __init__(self): self.magic = CPRT_MAGIC self.version = PROTOCOL_VERSION self.session_id = 0 self.data = b'' # Section 0: 调用方 UUID self.token = b'' # Section 1: 认证令牌 self.extra_data = b'' # Section 2: JSON 载荷 self.extra_data2 = b'' # Section 3: 扩展数据 def serialize(self): header = struct.pack( self.HEADER_FMT, self.magic, self.version, self.session_id, len(self.data), len(self.token), len(self.extra_data), 0, len(self.extra_data2), ) return header + self.data + self.token + self.extra_data + self.extra_data2 @classmethod def deserialize(cls, raw): if len(raw) < HEADER_SIZE: raise ValueError(f"数据不足: 需要 {HEADER_SIZE} 字节, 实际 {len(raw)}") pkt = cls() ( pkt.magic, pkt.version, pkt.session_id, d_len, t_len, e_len, _, e2_len, ) = struct.unpack(cls.HEADER_FMT, raw[:HEADER_SIZE]) if pkt.magic != CPRT_MAGIC: raise ValueError(f"协议魔数不匹配: 0x{pkt.magic:08X}") off = HEADER_SIZE pkt.data = raw[off:off + d_len]; off += d_len pkt.token = raw[off:off + t_len]; off += t_len pkt.extra_data = raw[off:off + e_len]; off += e_len pkt.extra_data2 = raw[off:off + e2_len] return pkt def get_json_payload(self): if self.extra_data: return self.extra_data.decode('utf-8', errors='replace') if self.data: return self.data.decode('utf-8', errors='replace') return '' def _recv_all(sock, size, timeout=DEFAULT_TIMEOUT): sock.settimeout(timeout) buf = b'' while len(buf) < size: try: chunk = sock.recv(size - len(buf)) except socket.timeout: raise TimeoutError(f"接收超时 ({timeout}s): {len(buf)}/{size} 字节") if not chunk: raise ConnectionError(f"连接断开: {len(buf)}/{size} 字节") buf += chunk return buf def send_rpc_packet(uds_path, packet, timeout=DEFAULT_TIMEOUT): """通过 Unix Domain Socket 发送 RpcPacket 并接收响应""" sock = socket.socket(socket.AF_UNIX, socket.SOCK_STREAM) sock.settimeout(timeout) try: sock.connect(uds_path) sock.sendall(packet.serialize()) resp_header = _recv_all(sock, HEADER_SIZE, timeout) ( _, _, _, d_len, t_len, e_len, _, e2_len, ) = struct.unpack(RpcPacket.HEADER_FMT, resp_header) total = d_len + t_len + e_len + e2_len resp_body = _recv_all(sock, total, timeout) if total > 0 else b'' return RpcPacket.deserialize(resp_header + resp_body) except FileNotFoundError: raise ConnectionError(f"UDS 不存在: {uds_path} (RPC 服务可能未运行)") except PermissionError: raise ConnectionError(f"无权限连接: {uds_path}") finally: sock.close() def build_reqid(): ts = int(time.time() * 1000000) return format(ts & 0xFFFFFFFFFFFFFFFF, '016x') def build_rpc_request(service, method, params=None, caller_uuid=None): if len(service) > MAX_SERVICE_NAME_LEN: raise ValueError(f"服务名超过 {MAX_SERVICE_NAME_LEN} 字节") if not service.startswith(SERVICE_PREFIX): raise ValueError(f'服务名必须以 "{SERVICE_PREFIX}" 开头') json_payload = { "data": { "req": f"{service}.{method}", "pid": os.getpid(), "reqid": build_reqid(), } } if params: json_payload["data"].update(params) pkt = RpcPacket() pkt.session_id = _next_session_id() pkt.data = (caller_uuid or str(uuid.uuid4()).upper()).encode('utf-8') pkt.extra_data = json.dumps(json_payload, separators=(',', ':')).encode('utf-8') return pkt class RpcClient: """trimrpc RPC 客户端 — 自动服务发现与调用""" def __init__(self, caller_uuid=None, caller_service="com.trim.pyclient", timeout=DEFAULT_TIMEOUT, broker_uds=None): self.uuid = caller_uuid or str(uuid.uuid4()).upper() self.caller_service = caller_service self.timeout = timeout self.broker_uds = broker_uds or self._find_broker() self.services = { BROKER_SERVICE: {"name": BROKER_SERVICE, "uds": self.broker_uds}, } def _find_broker(self): for p in BROKER_UDS_PATHS: if os.path.exists(p): return p return BROKER_UDS_PATHS[0] def apply_permission(self, target_services): if isinstance(target_services, str): target_services = [target_services] pkt = build_rpc_request( BROKER_SERVICE, "apply", {"services": target_services}, caller_uuid=self.uuid, ) resp_pkt = send_rpc_packet(self.broker_uds, pkt, self.timeout) resp_str = resp_pkt.get_json_payload() if not resp_str: raise RuntimeError("Broker 返回空响应") resp = json.loads(resp_str) resp_data = resp.get("data", resp) if resp_data.get("result") != "succ": raise RuntimeError( f"ApplyPermission 失败: {json.dumps(resp_data, ensure_ascii=False)}" ) for svc in resp_data.get("data", []): sid = svc.get("id", "") if sid: self.services[sid] = svc return resp_data.get("data", []) def call(self, service, method, params=None): svc_info = self.services.get(service) if not svc_info: self.apply_permission([service]) svc_info = self.services.get(service) if not svc_info: raise RuntimeError(f"服务未找到: {service}") uds_path = svc_info.get("uds", "") if not uds_path: raise RuntimeError(f"服务 {service} 无 UDS 路径") pkt = build_rpc_request(service, method, params, caller_uuid=self.uuid) token = svc_info.get("token", "") if token: pkt.token = token.encode('utf-8') resp_pkt = send_rpc_packet(uds_path, pkt, self.timeout) resp_str = resp_pkt.get_json_payload() return json.loads(resp_str) if resp_str else {} # ═══════════════════════════════════════════════════════════════════ # MCP 工具定义与实现 # ═══════════════════════════════════════════════════════════════════ MCP_TOOLS = [ { "name": "get_system_logs", "description": ( "获取 fnOS 系统事件日志。返回系统操作记录,包括用户登录、" "系统配置变更、SSH 连接等事件。支持分页和按模块筛选。" ), "inputSchema": { "type": "object", "properties": { "page": { "type": "integer", "description": "页码,从 1 开始", "default": 1, }, "pageSize": { "type": "integer", "description": "每页条数,范围 1-100", "default": 20, }, "module": { "type": "integer", "description": "模块筛选: -1 表示全部模块", "default": -1, }, }, }, }, { "name": "trimrpc_call", "description": ( "通用 fnOS trimrpc RPC 调用。通过 RPC Broker 调用 fnOS 上的" "任意已注册 RPC 服务。服务名必须以 'com.' 开头。" "示例: service='com.trim.storage', method='get_info'" ), "inputSchema": { "type": "object", "properties": { "service": { "type": "string", "description": "目标服务名,如 'com.trim.storage'", }, "method": { "type": "string", "description": "方法名,如 'get_info' 或 'common.list'", }, "params": { "type": "object", "description": "调用参数", "default": {}, }, }, "required": ["service", "method"], }, }, { "name": "discover_services", "description": ( "发现 fnOS 上可用的 RPC 服务。查询 RPC Broker 获取服务路由信息," "包括 UDS 路径、IP、类型、认证令牌等。" ), "inputSchema": { "type": "object", "properties": { "services": { "type": "array", "items": {"type": "string"}, "description": ( "要查询的服务名列表。" "示例: ['com.trim.storage', 'com.trim.main']" ), "default": [], }, }, }, }, ] # ── 全局 RPC 客户端 ── _rpc_client = None _rpc_client_lock = threading.Lock() _broker_uds_override = None # 由 --broker-uds 设置 def _get_client(): global _rpc_client with _rpc_client_lock: if _rpc_client is None: _rpc_client = RpcClient(broker_uds=_broker_uds_override) log.info(f"RPC 客户端初始化, Broker: {_rpc_client.broker_uds}") return _rpc_client # ── 工具处理函数 ── def tool_get_system_logs(arguments): page = max(1, arguments.get("page", 1)) page_size = max(1, min(100, arguments.get("pageSize", 20))) module = arguments.get("module", -1) result = _get_client().call( "com.trim.eventlogger", "common.list", {"pageSize": page_size, "page": page, "module": module}, ) return json.dumps(result, indent=2, ensure_ascii=False) def tool_trimrpc_call(arguments): service = arguments.get("service", "") method = arguments.get("method", "") params = arguments.get("params", {}) if not service: raise ValueError("缺少必填参数 'service'") if not method: raise ValueError("缺少必填参数 'method'") result = _get_client().call(service, method, params if params else None) return json.dumps(result, indent=2, ensure_ascii=False) def tool_discover_services(arguments): services = arguments.get("services", []) client = _get_client() if not services: try: pkt = build_rpc_request( BROKER_SERVICE, "list", {}, caller_uuid=client.uuid, ) resp_pkt = send_rpc_packet(client.broker_uds, pkt, client.timeout) resp_str = resp_pkt.get_json_payload() if resp_str: return json.dumps(json.loads(resp_str), indent=2, ensure_ascii=False) except Exception: pass services = [BROKER_SERVICE] result = client.apply_permission(services) return json.dumps(result, indent=2, ensure_ascii=False) TOOL_HANDLERS = { "get_system_logs": tool_get_system_logs, "trimrpc_call": tool_trimrpc_call, "discover_services": tool_discover_services, } # ═══════════════════════════════════════════════════════════════════ # MCP JSON-RPC 处理 # ═══════════════════════════════════════════════════════════════════ MCP_PROTOCOL_VERSION = "2024-11-05" SERVER_INFO = {"name": "trimrpc-mcp", "version": "1.0.0"} def process_jsonrpc(msg): """ 处理一条 JSON-RPC 2.0 消息,返回响应 dict 或 None (通知无需响应)。 """ msg_id = msg.get("id") method = msg.get("method", "") params = msg.get("params") or {} # 通知 (无 id) 不需要回复 if msg_id is None: log.debug(f"通知: {method}") return None def _result(result): return {"jsonrpc": "2.0", "id": msg_id, "result": result} def _error(code, message): return {"jsonrpc": "2.0", "id": msg_id, "error": {"code": code, "message": message}} if method == "initialize": return _result({ "protocolVersion": MCP_PROTOCOL_VERSION, "capabilities": {"tools": {}}, "serverInfo": SERVER_INFO, }) if method == "ping": return _result({}) if method == "tools/list": return _result({"tools": MCP_TOOLS}) if method == "tools/call": tool_name = params.get("name", "") arguments = params.get("arguments", {}) handler = TOOL_HANDLERS.get(tool_name) if not handler: return _error(-32602, f"未知工具: {tool_name}") try: text = handler(arguments) return _result({"content": [{"type": "text", "text": text}]}) except Exception as e: log.error(f"工具 {tool_name} 执行失败: {e}") return _result({ "content": [{"type": "text", "text": f"错误: {e}"}], "isError": True, }) return _error(-32601, f"方法不存在: {method}") # ═══════════════════════════════════════════════════════════════════ # HTTP 传输层 (Streamable HTTP) # # POST /mcp → 发送 JSON-RPC 请求, 直接返回 JSON 响应 # ═══════════════════════════════════════════════════════════════════ class ThreadedHTTPServer(ThreadingMixIn, HTTPServer): daemon_threads = True allow_reuse_address = True class McpRequestHandler(BaseHTTPRequestHandler): def log_message(self, fmt, *args): log.debug(f"{self.client_address[0]} {fmt % args}") def _cors_headers(self): self.send_header('Access-Control-Allow-Origin', '*') self.send_header('Access-Control-Allow-Methods', 'GET, POST, DELETE, OPTIONS') self.send_header('Access-Control-Allow-Headers', 'Content-Type') def _json_response(self, code, obj): body = json.dumps(obj, ensure_ascii=False).encode('utf-8') self.send_response(code) self.send_header('Content-Type', 'application/json') self.send_header('Content-Length', str(len(body))) self._cors_headers() self.end_headers() self.wfile.write(body) def do_OPTIONS(self): self.send_response(200) self._cors_headers() self.end_headers() def do_DELETE(self): self.send_response(200) self._cors_headers() self.end_headers() def do_GET(self): self._json_response(200, { "name": SERVER_INFO["name"], "version": SERVER_INFO["version"], "status": "running", "tools": [t["name"] for t in MCP_TOOLS], }) def do_POST(self): content_len = int(self.headers.get('Content-Length', 0)) if content_len == 0: self._json_response(400, {"error": "Empty body"}) return try: body = self.rfile.read(content_len) msg = json.loads(body) except (json.JSONDecodeError, UnicodeDecodeError) as e: self._json_response(400, {"error": f"Invalid JSON: {e}"}) return log.debug(f"← {msg.get('method', '?')} (id={msg.get('id')})") response = process_jsonrpc(msg) if response is not None: self._json_response(200, response) else: self.send_response(202) self._cors_headers() self.end_headers() # ═══════════════════════════════════════════════════════════════════ # 入口 # ═══════════════════════════════════════════════════════════════════ def main(): import argparse parser = argparse.ArgumentParser( description="trimrpc MCP Server — fnOS 系统管理工具", formatter_class=argparse.RawDescriptionHelpFormatter, epilog=""" 示例: %(prog)s # 默认 0.0.0.0:9800 %(prog)s --port 8080 # 自定义端口 %(prog)s --host 127.0.0.1 # 仅允许本机访问 %(prog)s --broker-uds /tmp/rpc/rpcbroker MCP 配置: {"mcpServers": {"fnos": {"url": "http://:9800/mcp"}}} """, ) parser.add_argument('--host', default='0.0.0.0', help='监听地址 (默认: 0.0.0.0)') parser.add_argument('--port', type=int, default=9800, help='监听端口 (默认: 9800)') parser.add_argument('--broker-uds', default=None, help='指定 RPC Broker UDS 路径') parser.add_argument('--list-tools', action='store_true', help='列出可用工具并退出') parser.add_argument('-v', '--verbose', action='store_true', help='调试日志') args = parser.parse_args() if args.verbose: logging.getLogger().setLevel(logging.DEBUG) if args.list_tools: print("trimrpc MCP Server 可用工具:\n") for t in MCP_TOOLS: print(f" {t['name']}") print(f" {t['description']}") schema = t.get("inputSchema", {}) props = schema.get("properties", {}) if props: print(" 参数:") for k, v in props.items(): req = "必填" if k in schema.get("required", []) else "可选" desc = v.get("description", "") default = v.get("default") default_str = f", 默认={default}" if default is not None else "" print(f" {k} ({req}{default_str}): {desc}") print() return global _broker_uds_override _broker_uds_override = args.broker_uds server = ThreadedHTTPServer((args.host, args.port), McpRequestHandler) host_display = args.host if args.host != '0.0.0.0' else '' print(f"trimrpc MCP Server 已启动") print(f" 监听: http://{args.host}:{args.port}") print(f" 端点: POST http://{host_display}:{args.port}/mcp") print(f" 工具: {', '.join(t['name'] for t in MCP_TOOLS)}") if args.broker_uds: print(f" Broker: {args.broker_uds}") print(f'\n MCP 配置: {{"mcpServers": {{"fnos": {{"url": "http://{host_display}:{args.port}/mcp"}}}}}}') print() try: server.serve_forever() except KeyboardInterrupt: print("\n正在关闭...") server.shutdown() if __name__ == '__main__': main() ``` </details> # 部署 将代码复制到任意可执行目录中,这里文件名为: `report-trimrpc-mcp-202603062124.py` 直接运行即可: ```text Linux fnos 6.12.18-trim #211 SMP PREEMPT_DYNAMIC Fri Jan 30 17:47:53 CST 2026 x86_64 Last login: Fri Mar 6 23:21:46 2026 from 192.168.2.12 username@fnos:~$ python3 report-trimrpc-mcp-202603062124.py trimrpc MCP Server 已启动 监听: http://0.0.0.0:9800 端点: POST http://:9800/mcp 工具: get_system_logs, trimrpc_call, discover_services MCP 配置: {"mcpServers": {"fnos": {"url": "http://:9800/mcp"}}} ``` 启动后,一个简单的 MCP HTTP 服务就已经运行,并对外提供三个工具接口: get_system_logs:获取系统日志 trimrpc_call:通用 RPC 调用 discover_services:发现可用 RPC 服务 # 给AI配置MCP 使用 Claude CLI 添加 MCP 服务: ```shell claude mcp add --transport http fnos http://192.168.2.254:9800/mcp ``` 执行命令后,CLI 会将 MCP 配置写入本地配置文件: ```text PS C:\> claude mcp add --transport http fnos http://192.168.2.254:9800/mcp Added HTTP MCP server fnos with URL: http://192.168.2.254:9800/mcp to local config File modified: C:\Users\admin\.claude.json [project: C:\] PS C:\> claude mcp get fnos fnos: Scope: Local config (private to you in this project) Status: ✗ Failed to connect Type: http URL: http://192.168.2.254:9800/mcp To remove this server, run: claude mcp remove "fnos" -s local Assertion failed: !(handle->flags & UV_HANDLE_CLOSING), file src\win\async.c, line 76 ``` # 调用MCP能力 我们先看看当前的日志 ![img.png](/2026/03/062311_mcp_in_fnos/img.png) 配置完成后,在 Claude CLI 中即可直接调用 MCP 提供的能力: 让我们直接获取一下日志信息 ```text PS E:\workspace\fnos\demod1> claude ╭─── Claude Code v2.1.70 ───────────────────────────────────────────────────────────────────────────────────────────╮ │ │ Tips for getting started │ │ Welcome back! │ Run /init to create a CLAUDE.md file with instructions for Claude │ │ │ ───────────────────────────────────────────────────────────────── │ │ │ Recent activity │ │ ▐▛███▜▌ │ No recent activity │ │ ▝▜█████▛▘ │ │ │ ▘▘ ▝▝ │ │ │ Opus 4.6 (1M context) · API Usage Billing │ │ │ E:\workspace\fnos\demod1 │ │ ╰───────────────────────────────────────────────────────────────────────────────────────────────────────────────────╯ Welcome to Opus 4.6 ❯ /mcp ⎿ MCP dialog dismissed ❯ 获取fnos的mcp全部功能,以列表形式列出 ● Based on the available MCP tools, here are all fnOS MCP functions: ┌─────┬──────────────────────────────┬──────────────────────────────────────────────────────────────────────────────────────────────────────────────┐ │ # │ Function │ Description │ ├─────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ 1 │ mcp__fnos__get_system_logs │ 获取 fnOS 系统事件日志。返回系统操作记录,包括用户登录、系统配置变更、SSH 连接等事件。支持分页和按模块筛选。 │ ├─────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ 2 │ mcp__fnos__trimrpc_call │ 通用 fnOS trimrpc RPC 调用。通过 RPC Broker 调用 fnOS 上的任意已注册 RPC 服务。服务名必须以 com. 开头。 │ ├─────┼──────────────────────────────┼──────────────────────────────────────────────────────────────────────────────────────────────────────────────┤ │ 3 │ mcp__fnos__discover_services │ 发现 fnOS 上可用的 RPC 服务。查询 RPC Broker 获取服务路由信息,包括 UDS 路径、IP、类型、认证令牌等。 │ └─────┴──────────────────────────────┴──────────────────────────────────────────────────────────────────────────────────────────────────────────────┘ 参数详情 get_system_logs: - module (int, 默认 -1) — 模块筛选,-1 表示全部模块 - page (int, 默认 1) — 页码,从 1 开始 - pageSize (int, 默认 20) — 每页条数,范围 1-100 trimrpc_call: - service (string, 必填) — 目标服务名,如 com.trim.storage - method (string, 必填) — 方法名,如 get_info - params (object, 默认 {}) — 调用参数 discover_services: - services (string[], 默认 []) — 要查询的服务名列表,如 ['com.trim.storage'] 核心能力是通过 discover_services 发现可用服务,再通过 trimrpc_call 调用具体服务方法,以及通过 get_system_logs 查看系统日志。 ❯ 获取最新的12条日志 ● fnos - get_system_logs (MCP)(page: 1, pageSize: 12) ⎿  { "data": { "data": { … +73 lines (ctrl+o to expand) ● 最新 12 条系统日志(共 3067 条): ┌─────┬─────────────────────┬──────────┬──────┬──────────────────────────────────────┐ │ # │ 时间 │ 用户 │ 模块 │ 事件内容 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 1 │ 2026-03-04 18:44:03 │ username │ 登录 │ 登录成功(via token) IP:::1 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 2 │ 2026-03-04 18:02:23 │ username │ 登录 │ 登录成功(via SSH) IP:192.168.2.12 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 3 │ 2026-03-04 18:01:53 │ username │ 登录 │ 登录成功(via SSH) IP:192.168.2.12 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 4 │ 2026-03-04 18:01:45 │ username │ 登录 │ 登录成功(via SSH) IP:192.168.2.12 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 5 │ 2026-03-04 16:36:52 │ username │ 登录 │ 登录成功(via token) IP:::1 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 6 │ 2026-03-04 04:59:03 │ username │ 登录 │ 登录成功(via token) IP:192.168.2.12 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 7 │ 2026-03-03 20:10:25 │ username │ 登录 │ 登录成功 IP:::1 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 8 │ 2026-03-03 16:58:12 │ username │ 登录 │ 登录成功(via token) IP:::1 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 9 │ 2026-03-03 16:32:14 │ username │ 登录 │ 登录成功 IP:::1 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 10 │ 2026-03-03 09:36:20 │ system │ 系统 │ 应用 影视 启用成功 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 11 │ 2026-03-03 09:36:14 │ system │ 系统 │ 应用 影视 更新成功 │ ├─────┼─────────────────────┼──────────┼──────┼──────────────────────────────────────┤ │ 12 │ 2026-03-03 09:34:12 │ username │ 登录 │ 登录成功(via token) IP:192.168.2.217 │ └─────┴─────────────────────┴──────────┴──────┴──────────────────────────────────────┘ ❯ discover_services ● fnos - discover_services (MCP) ⎿  { "data": { "errno": 100000004, … +6 lines (ctrl+o to expand) ● 空列表查询失败,让我尝试查询一些常见的 fnOS 服务。 ● fnos - discover_services (MCP)(services: ["com.trim.storage","com.trim.main","com.trim.eventlogger","com.trim.docker","com.trim.network","com.trim.user","com.trim.system ","com.trim.app","com.trim.filemanager","com.trim.share","com.trim.rpcbroker","com.trim.notify","com.trim.backup","com.trim.disk","com.trim .power","com.trim.update","com.trim.samba","com.trim.nfs","com.trim.webdav","com.trim.ddns"]) ⎿  [ { "id": "com.trim.main", … +31 lines (ctrl+o to expand) ● 发现了部分服务,让我再探索更多。 ● fnos - discover_services (MCP)(services: ["com.trim.storage.manager","com.trim.usb","com.trim.raid","com.trim.smart","com.trim.ftp","com.trim.rsync","com.trim.vpn","com. trim.firewall","com.trim.dns","com.trim.certificate","com.trim.task","com.trim.cron","com.trim.log","com.trim.monitor","com.trim.alert","co m.trim.plugin","com.trim.appstore","com.trim.vm","com.trim.photo","com.trim.music"]) ⎿  [] ● fnos - discover_services (MCP)(services: ["com.trim.filemanager.ui","com.trim.file","com.trim.smb","com.trim.afp","com.trim.iscsi","com.trim.power.manager","com.trim.acc ount","com.trim.auth","com.trim.gateway","com.trim.proxy","com.trim.scheduler","com.trim.media","com.trim.download","com.trim.sync","com.tr im.cloud","com.trim.notification","com.trim.hardware","com.trim.firmware","com.trim.config","com.trim.service"]) ⎿  [] ● 以下是在 fnOS 上发现的可用 RPC 服务: ┌─────┬──────────────────────┬─────────────────────────┬───────────────────────────────┬──────┐ │ # │ 服务 ID │ 名称 │ UDS 路径 │ 类型 │ ├─────┼──────────────────────┼─────────────────────────┼───────────────────────────────┼──────┤ │ 1 │ com.trim.main │ TRIM Service │ /run/trim_srv.socket │ 1 │ ├─────┼──────────────────────┼─────────────────────────┼───────────────────────────────┼──────┤ │ 2 │ com.trim.eventlogger │ Logger Service │ /run/trim_app_cgi/eventlogger │ 0 │ ├─────┼──────────────────────┼─────────────────────────┼───────────────────────────────┼──────┤ │ 3 │ com.trim.network │ Network Manager Service │ /run/trim_app_cgi/network │ 0 │ ├─────┼──────────────────────┼─────────────────────────┼───────────────────────────────┼──────┤ │ 4 │ com.trim.share │ Share File Service │ /run/trim_app_cgi/share │ 0 │ └─────┴──────────────────────┴─────────────────────────┴───────────────────────────────┴──────┘ 共发现 4 个已注册的 RPC 服务: - com.trim.main — 核心主服务 - com.trim.eventlogger — 事件日志服务(系统日志查询) - com.trim.network — 网络管理服务 - com.trim.share — 文件共享服务 ───────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ▪▪▪ ─ ❯ 调用 com.trim.main 的方法看看有哪些功能 ─────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────────── ? for shortcuts ``` # 结束语 MCP 的出现,使大模型具备了调用外部服务的能力。 从某种意义上来说,这让模型不再只是“回答问题”,而是开始具备与真实系统交互的能力。 不过,这也带来了新的问题。 如果某一天 AI 误操作,或者逻辑出现偏差,通过 MCP 调用接口删除了系统中的关键数据,该如何处理? 在无法很好解决这种风险之前,大模型与现实系统之间的深度交互,仍然需要谨慎对待。 不过在考虑AI之前,不知道本地应用如果直接调用trimrpc会有应用中心上架的合规风险吗? ```text username@fnos:~$ ls -la /run/trim_app_cgi/rpcbroker srwxrw-rw- 1 root root 0 Feb 27 10:37 /run/trim_app_cgi/rpcbroker ```
这些都是废话,不建议浪费时间看 # trimrpc ## 0. 术语缩写 | 缩写 | 含义 | |------|------| | SSO | Small String Optimization, libstdc++ 在 `sizeof(std::string) = 32` 时内联 15 字节以下字符串 | | RB-tree | `std::_Rb_tree`, GCC libstdc++ 红黑树实现, 用于 `std::map` | | DCL | Double-Checked Locking, 双重检查锁定单例模式 | | UDS | Unix Domain Socket | | vtable | C++ 虚函数表, 通过 `[rdi]` 间接调用 | | PLT/GOT | Procedure Linkage Table / Global Offset Table, ELF 延迟绑定机制 | --- ## 1. 体系结构概述 trimrpc 是 fnOS 自研的本地 IPC 框架。客户端通过 `RpcClient` API 向目标服务发起 RPC 调用, 底层通过 UDS 传输二进制编码的数据包。框架支持双协议栈: **RpcPacket** (30 字节固定头, 面向 AppCgi 型服务, `ServiceType=0`) 和 **TrimSrv** (12 字节头, 面向 TrimSrv 型服务, `ServiceType=1`)。 所有服务通过中央 Broker (`/run/trim_app_cgi/rpcbroker`) 进行注册与发现。客户端先向 Broker 申请权限获取目标服务的 UDS 路径和服务类型, 随后直连目标服务进行 RPC 通信 — 这是服务发现模式, Broker 不参与后续数据转发。 ``` 调用层 传输层 ┌───────────────────────┐ ┌──────────────────────────────┐ │ RpcClient (0x259b0) │ │ RpcConnections (singleton) │ │ ├ Call() 0x25bf0 │ ─Caller──▶│ ├ Init() 0x196b0 │ │ ├ Apply() 0x27740 │ 0x15480 │ ├ Connect() 0x1a920 │ │ └ Verify 0x256c0 │ │ ├ Send() 0x192d0 │ └───────────────────────┘ │ └ OnRecv() 0x17490 │ └───────────┬────────────────┘ │ ne* 网络引擎 ▼ Unix Domain Socket /var/run/rpc/ ``` --- ## 2. RpcPacket 二进制协议解析 ### 2.1 RpcPacket::RpcPacket() — 构造函数 @ 0x1DDF0 此函数初始化一个 0xA8 字节的 RpcPacket 对象, 写入 magic 常量和协议版本, 并设置四个 SSO 字符串的内联指针。 ```asm ; trimrpc::RpcPacket::RpcPacket(void) ; 对象大小 = 0xA8 (168 字节) ; rdi = this 指针 _ZN7trimrpc9RpcPacketC2Ev: 1ddf0 mov rax, cs:_ZTVN7trimrpc9RpcPacketE_ptr ; 加载 RpcPacket vtable 地址 1ddf7 mov dword ptr [rdi+8], 54525043h ; [this+0x08] = magic = 0x54525043 ; 即 ASCII "CPRT" (little-endian) 1ddfe mov qword ptr [rdi+10h], 0 ; [this+0x10] = session_id = 0 1de06 add rax, 10h ; 跳过 RTTI 偏移, 指向第一个虚函数 1de0a mov qword ptr [rdi+18h], 0 ; [this+0x18] = data_len/token_len = 0 1de12 mov [rdi], rax ; [this+0x00] = vtable 指针 ; version 字段 (2 字节) 1de15 mov eax, 1 1de1a mov [rdi+0Ch], ax ; [this+0x0C] = version = 1 ; 初始化四个 SSO 字符串的内联缓冲区指针 ; 每个 std::string 占 32 字节: [ptr(8) + size(8) + inline_buf(16)] 1de1e lea rax, [rdi+38h] ; data 字符串的内联缓冲区 1de22 mov [rdi+28h], rax ; [this+0x28].ptr = &this->inline_buf_data 1de26 lea rax, [rdi+58h] ; token 字符串 1de2a mov [rdi+48h], rax ; [this+0x48].ptr = &this->inline_buf_token 1de2e lea rax, [rdi+78h] ; extra_data 字符串 1de32 mov [rdi+68h], rax ; [this+0x68].ptr = &this->inline_buf_extra 1de36 lea rax, [rdi+98h] ; extra_data2 字符串 1de3d mov [rdi+88h], rax ; [this+0x88].ptr = &this->inline_buf_extra2 ; reserved 字段 1de44 mov eax, cs:dword_315F4 ; 加载全局默认 reserved 值 1de4a mov dword ptr [rdi+24h], 0 ; [this+0x24] = extra_data2_len = 0 ; 将四个字符串置空 (size=0, inline_buf[0]='\0') 1de51 mov qword ptr [rdi+30h], 0 ; data.size = 0 1de59 mov byte ptr [rdi+38h], 0 ; data.inline_buf[0] = '\0' 1de5d mov qword ptr [rdi+50h], 0 ; token.size = 0 1de65 mov byte ptr [rdi+58h], 0 ; token.inline_buf[0] = '\0' 1de69 mov qword ptr [rdi+70h], 0 ; extra_data.size = 0 1de71 mov byte ptr [rdi+78h], 0 ; extra_data.inline_buf[0] = '\0' 1de75 mov qword ptr [rdi+90h], 0 ; extra_data2.size = 0 1de80 mov byte ptr [rdi+98h], 0 ; extra_data2.inline_buf[0] = '\0' 1de87 mov [rdi+20h], eax ; [this+0x20] = reserved (从全局) 1de8a retn ``` **对象内存布局 (0xA8 字节)**: ``` 偏移 大小 字段 ───────── ───── ────────────────────────────── 0x00 8 vtable* 0x08 4 magic = 0x54525043 ("CPRT") 0x0C 2 version = 1 0x0E 2 (padding) 0x10 8 session_id (8 字节会话标识) 0x18 2 data_len 0x1A 2 token_len 0x1C 4 extra_data_len 0x20 4 reserved 0x24 4 extra_data2_len 0x28 32 std::string data (JSON 请求/响应) 0x48 32 std::string token (UUID / 认证令牌) 0x68 32 std::string extra_data 0x88 32 std::string extra_data2 ``` ### 2.2 RpcPacket::DeserializeHeader() @ 0x1DEA0 解析 30 字节的网络字节流到 RpcPacket 对象的头部字段。 ```asm ; trimrpc::RpcPacket::DeserializeHeader(const char* buf, uint len) ; rdi = this, rsi = buf 指向 30 字节原始头部 _ZN7trimrpc9RpcPacket17DeserializeHeaderEPKcj: 1dea0 mov eax, [rsi] ; eax = *(uint32_t*)(buf+0) = magic 1dea2 mov [rdi+8], eax ; this->magic = magic 1dea5 movzx eax, word ptr [rsi+4] ; eax = *(uint16_t*)(buf+4) = version 1dea9 mov [rdi+0Ch], ax ; this->version = version 1dead mov rax, [rsi+6] ; rax = *(uint64_t*)(buf+6) = session_id 1deb1 mov [rdi+10h], rax ; this->session_id = session_id 1deb5 movzx eax, word ptr [rsi+0Eh] ; eax = *(uint16_t*)(buf+14) = data_len 1deb9 mov [rdi+18h], ax ; this->data_len = data_len 1debd movzx eax, word ptr [rsi+10h] ; eax = *(uint16_t*)(buf+16) = token_len 1dec1 mov [rdi+1Ah], ax ; this->token_len = token_len 1dec5 mov eax, [rsi+12h] ; eax = *(uint32_t*)(buf+18) = extra_data_len 1dec8 mov [rdi+1Ch], eax ; this->extra_data_len = extra_data_len 1decb mov eax, [rsi+16h] ; eax = *(uint32_t*)(buf+22) = reserved/flags 1dece mov [rdi+20h], eax ; this->reserved = reserved 1ded1 mov eax, [rsi+1Ah] ; eax = *(uint32_t*)(buf+26) = extra_data2_len 1ded4 mov [rdi+24h], eax ; this->extra_data2_len = extra_data2_len 1ded7 retn ``` **头部线格式 (30 字节, 无对齐填充)**: ``` Byte: 0 1 2 3 4 5 6 7 8 9 A B C D E F 10 11 12 13 14 15 16 17 18 19 1A 1B 1C 1D ├──magic──┤ ├vers┤ ├───────session_id────────┤ ├dlen┤ ├tlen┤ ├─extra_len─┤ ├─reserved──┤ ├─extra2_len┤ ``` ### 2.3 RpcPacket::Serialize() @ 0x1DC30 将 RpcPacket 对象序列化为网络传输的二进制字符串: 30 字节头 + data + token + extra_data + extra_data2。 ```asm ; trimrpc::RpcPacket::Serialize(void) -> std::string ; rdi = 返回值 (std::string*), rsi = this _ZN7trimrpc9RpcPacket9SerializeB5cxx11Ev: 1dc30 push r12 1dc32 push rbp 1dc33 mov rbp, rdi ; rbp = 返回 string 对象指针 1dc36 push rbx 1dc37 mov rbx, rsi ; rbx = this (RpcPacket*) ; 将各数据段长度写入头部字段 1dc3a mov rax, [rsi+30h] ; rax = data.size() 1dc3e mov rcx, [rsi+50h] ; rcx = token.size() 1dc42 mov rdi, [rbx] ; rdi = vtable* 1dc45 mov [rsi+18h], ax ; this->data_len = (uint16_t)data.size() 1dc49 mov rdx, [rbx+70h] ; rdx = extra_data.size() 1dc4d mov [rsi+1Ah], cx ; this->token_len = (uint16_t)token.size() 1dc51 mov r8, [rdi+28h] ; r8 = vtable->data_length (虚函数指针) 1dc55 lea rdi, data_length_func ; rdi = &RpcPacket::data_length 静态地址 1dc5c mov rsi, [rsi+90h] ; rsi = extra_data2.size() 1dc63 mov [rbx+1Ch], edx ; this->extra_data_len = extra_data.size() 1dc66 mov [rbx+24h], esi ; this->extra_data2_len = extra_data2.size() ; 检查是否是基类的 data_length 实现 (非虚覆盖) 1dc69 cmp r8, rdi 1dc6c jnz loc_1DDD0 ; 若被覆盖, 走虚调用路径 ; 计算总长度 = 0x1E + data_len + token_len + extra_data_len + extra_data2_len 1dc72 movzx eax, ax ; data_len 1dc75 movzx ecx, cx ; token_len 1dc78 add edx, esi ; extra_data_len + extra_data2_len 1dc7a add eax, ecx ; data_len + token_len 1dc7c add eax, edx ; + extra sizes 1dc7e lea rdi, [rbp+10h] ; rdi = 返回 string 的内联缓冲区 1dc82 lea r12d, [rax+1Eh] ; r12 = total_len = payload + 30 字节头 1dc86 mov [rbp+0], rdi ; string.ptr = inline buf (SSO 尝试) 1dc8a cmp r12, 0Fh 1dc8e ja loc_1DDA0 ; 超过 15 字节, 堆分配 ; 分配完毕后填充 30 字节头部 1dcaa mov eax, [rbx+8] ; magic 1dcad add rdi, 1Eh ; rdi 指向头部之后 = 数据区起始 1dcb1 mov [rdi-1Eh], eax ; 写入 magic @ offset 0 1dcb4 movzx eax, word ptr [rbx+0Ch] 1dcb8 mov [rdi-1Ah], ax ; 写入 version @ offset 4 1dcbc mov rax, [rbx+10h] 1dcc0 mov [rdi-18h], rax ; 写入 session_id @ offset 6 1dcc4 movzx eax, word ptr [rbx+18h] 1dcc8 mov rdx, [rbx+30h] ; rdx = data.size() 1dccc mov [rdi-10h], ax ; 写入 data_len @ offset 14 1dcd0 movzx eax, word ptr [rbx+1Ah] 1dcd4 mov [rdi-0Eh], ax ; 写入 token_len @ offset 16 1dcd8 mov eax, [rbx+1Ch] 1dcdb mov [rdi-0Ch], eax ; 写入 extra_data_len @ offset 18 1dcde mov eax, [rbx+20h] 1dce1 mov [rdi-8], eax ; 写入 reserved @ offset 22 1dce4 mov eax, [rbx+24h] 1dce7 mov [rdi-4], ax ; 写入 extra_data2_len @ offset 26 ; (注: 仅写 2 字节, 高 2 字节来自对齐) ; 依次 memcpy 四个数据段 1dceb test rdx, rdx ; data.size() == 0? 1dcee jnz loc_1DD80 ; 有数据则 memcpy data.c_str() 1dcf4 mov rdx, [rbx+50h] ; token.size() 1dcf8 test rdx, rdx 1dcfb jnz loc_1DD60 ; memcpy token 1dcfd mov rdx, [rbx+70h] ; extra_data.size() 1dd01 test rdx, rdx 1dd04 jnz loc_1DD38 ; memcpy extra_data 1dd06 mov rdx, [rbx+90h] ; extra_data2.size() 1dd0d test rdx, rdx 1dd10 jnz loc_1DD20 ; memcpy extra_data2 1dd12 mov rax, rbp ; 全部为空, 直接返回 1dd15 pop rbx 1dd16 pop rbp 1dd17 pop r12 1dd19 retn ``` ### 2.4 TrimSrvResponsePacket::DeserializeHeader() @ 0x21320 TrimSrv 协议的 12 字节头部解析。协议设计极简 — 无魔数校验, 无版本字段。 ```asm ; trimrpc::TrimSrvResponsePacket::DeserializeHeader(const char* buf, uint len) ; rdi = this, rsi = buf (12 字节), edx = len _ZN7trimrpc21TrimSrvResponsePacket17DeserializeHeaderEPKcj: 21320 push r14 21322 push r13 21324 push r12 21326 mov r12d, edx ; r12d = len (保存用于后续 memcpy) 21329 push rbp 2132a mov rbp, rsi ; rbp = buf 2132d push rbx 2132e mov rbx, rdi ; rbx = this ; 解析 12 字节头部: ; [0..3] uint32 packet_size — 整包大小(含头部 12 字节) ; [4..11] uint64 session_id 21335 mov rax, [rsi+4] ; rax = *(uint64_t*)(buf+4) = session_id 21339 mov r13d, [rsi] ; r13d = *(uint32_t*)(buf+0) = packet_size 21341 mov [rdi+28h], rax ; this->session_id = session_id (offset 0x28) ; 将 packet_size 转换为字符串存入 this->data ; (后续通过 data_length() 虚函数返回 packet_size - 12) ; ... SSO 字符串赋值逻辑 ... 21345 lea r14, [rsp+48h+src+8] 21349 cmp r13, 0Fh 2134d ja loc_21408 ; 大于 SSO 阈值则堆分配 ; ... 字符串赋值与 memcpy 完成 ... ; 最终将原始 buf 数据 memcpy 到 this->data 区 213ca add rsp, 20h 213ce mov edx, r12d ; n = len 213d1 mov rsi, rbp ; src = buf 213d4 pop rbx 213dc jmp _memcpy ; 尾调用 memcpy ``` ### 2.5 TrimSrvRequestPacket::Serialize() @ 0x163E0 TrimSrv 请求包的序列化极其简单 — 直接返回内部 data 字符串的拷贝, 无额外包装。 ```asm ; trimrpc::TrimSrvRequestPacket::Serialize(void) -> std::string ; rdi = 返回 string*, rsi = this _ZN7trimrpc20TrimSrvRequestPacket9SerializeB5cxx11Ev: 163e0 push r12 163e2 push rbp 163e3 push rbx 163e4 mov rbp, [rsi+10h] ; rbp = this->data.size() (offset 0x10) 163e8 mov rbx, rdi ; rbx = 返回值指针 163eb add rdi, 10h 163ef mov [rbx], rdi ; return_string.ptr = &inline_buf 163f2 mov r12, [rsi+8] ; r12 = this->data.c_str() (offset 0x08) 163f6 cmp rbp, 0Fh 163fa ja loc_16448 ; 大于 15 字节 → 堆分配 163fc cmp rbp, 1 16400 jnz loc_16420 ; != 1 → 可能为 0 或需 memcpy ; 单字节快速路径 16402 movzx eax, byte ptr [r12] ; 读取唯一字节 16407 mov [rbx+10h], al ; 写入内联缓冲区 1640a mov [rbx+8], rbp ; return_string.size = 1 1640e mov rax, rbx 16411 mov byte ptr [rdi+rbp], 0 ; 追加 '\0' 16415 pop rbx 16416 pop rbp 16417 pop r12 16419 retn ; 多字节路径 16420 test rbp, rbp ; size == 0? 16423 jz loc_1640A ; 是, 直接设 size=0 16425 mov rdx, rbp ; n = size 16428 mov rsi, r12 ; src = data.c_str() 1642b call _memcpy ; memcpy(inline_buf, src, n) 16430 mov rdi, [rbx] 16433 mov [rbx+8], rbp ; return_string.size = n 16437 mov rax, rbx 1643a mov byte ptr [rdi+rbp], 0 ; 追加 '\0' 1643e pop rbx 1643f pop rbp 16440 pop r12 16442 retn ; 堆分配路径 (size > 15) 16448 test rbp, rbp 1644b js loc_1647D ; 负值 → basic_string::_M_create 异常 1644d mov rdi, rbp 16450 add rdi, 1 ; 分配 size+1 字节 16456 call __Znwm ; operator new(size+1) 1645b mov [rbx+10h], rbp ; capacity = size 1645f mov rdx, rbp 16462 mov rsi, r12 16465 mov [rbx], rax ; ptr = heap_buf 16468 mov rdi, rax 1646b call _memcpy 16470 mov rdi, [rbx] 16473 jmp loc_16433 ; 设 size 并追加 '\0' ``` --- ## 3. RpcClient — 公共 API 层 ### 3.1 RpcClient::RpcClient() 构造函数 @ 0x259B0 ```asm ; trimrpc::RpcClient::RpcClient(const string& uuid, const string& service_name) ; rdi = this (0x88 字节), rsi = &uuid, rdx = &service_name _ZN7trimrpc9RpcClientC2ERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_: 259b0 push r15 259b2 push r14 259b4 lea r14, [rdi+18h] ; r14 → this->uuid 的内联缓冲区 259b8 push r13 259ba mov r13, rdx ; r13 = &service_name 259bd push r12 259bf mov r12, rsi ; r12 = &uuid 259c2 push rbp 259c3 push rbx 259c4 mov rbx, rdi ; rbx = this 259c7 sub rsp, 8 ; 初始化 caller 指针为 NULL 259cb mov rbp, [rsi+8] ; rbp = uuid.size() 259cf mov [rdi+8], r14 ; this->uuid.ptr = &inline_buf 259d3 mov qword ptr [rdi], 0 ; this->caller_ = nullptr (offset 0) 259da mov r15, [rsi] ; r15 = uuid.data() 259dd cmp rbp, 0Fh 259e1 ja loc_25B20 ; uuid > 15 字节 → 堆分配 ; uuid SSO 拷贝 (size == 1 快速路径) 259e7 cmp rbp, 1 259eb jnz loc_25B10 259f1 movzx eax, byte ptr [r15] 259f5 mov [rdi+18h], al ; inline_buf[0] = uuid[0] 259f8 mov rax, r14 259fb mov [rbx+10h], rbp ; this->uuid.size = 1 259ff mov byte ptr [rax+rbp], 0 ; '\0' 终止 ; 拷贝 service_name 到 this+0x28 25a03 lea rbp, [rbx+38h] ; service_name 内联缓冲区 25a07 mov [rbx+28h], rbp 25a0b mov r15, [r13+0] ; service_name.data() 25a0f mov r13, [r13+8] ; service_name.size() ; ... SSO 拷贝逻辑同上 ... ; 分配内部 Caller 对象 (0xD0 字节) 25a3d pxor xmm0, xmm0 25a42 mov edi, 0D0h ; sizeof(Caller) = 208 ; ... call operator new + Caller::Caller() ... ; 最终 this->caller_ = new Caller(uuid) ; 初始化其余字段 ; [this+0x48] = error_code = 0 ; [this+0x50] = response_string (SSO 空串) ; [this+0x70] = response shared_ptr = {nullptr, nullptr} ; [this+0x84] = pid = getpid() ``` **RpcClient 对象布局 (0x88 = 136 字节)**: ``` 偏移 大小 字段 ───────── ───── ────────────────────────── 0x00 8 Caller* 指向内部 Caller 对象 0x08 32 std::string uuid 0x28 32 std::string service_name 0x48 4 int32_t error_code 0x4C 4 (padding) 0x50 32 std::string response_string 0x70 16 shared_ptr response (Response 对象) 0x80 4 int32_t last_call_error 0x84 4 int32_t pid (= getpid()) ``` ### 3.2 RpcClient::VerifyServiceId() @ 0x256C0 验证 RPC 目标服务名的合法性。仅执行两项检查: 长度上限和固定前缀。 ```asm ; trimrpc::RpcClient::VerifyServiceId(const string& service_name) ; rdi = this, rsi = &service_name ; 返回: eax = 1 (合法) 或 0 (非法, 并设置 error_code) _ZN7trimrpc9RpcClient15VerifyServiceIdERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE: 256c0 sub rsp, 8 ; —— 检查 1: 长度 ≤ 128 —— 256c4 mov rax, [rsi+8] ; rax = service_name.size() 256c8 cmp rax, 80h ; 与 128 比较 256ce ja loc_25718 ; 超过 → 错误码 5 ; —— 检查 2: 前缀 "com." —— 256d0 mov rdx, [rsi] ; rdx = service_name.data() 256d3 cmp rax, 3 ; size <= 3 → 不可能以 "com." 开头 256d7 ja loc_25700 ; size >= 4, 可以检查 ; 验证失败 → 错误码 6 256d9 mov dword ptr [rdi+80h], 6 ; this->last_call_error = 6 256e3 lea rsi, aInvalidClientN ; "Invalid client name" 256ea mov edi, 3 ; LOG_ERR 256ef xor eax, eax 256f1 call _syslog ; syslog(LOG_ERR, "Invalid client name") 256f6 xor eax, eax ; return 0 (验证失败) 256f8 add rsp, 8 256fc retn ; 检查前 4 字节 == "com." (0x2E6D6F63, little-endian) 25700 cmp dword ptr [rdx], 2E6D6F63h ; *(uint32_t*)data == "com." 25706 jnz loc_256D9 ; 不匹配 → 错误码 6 25708 mov eax, 1 ; return 1 (验证通过) 2570d add rsp, 8 25711 retn ; 长度超限 → 错误码 5 25718 mov dword ptr [rdi+80h], 5 ; this->last_call_error = 5 25722 lea rsi, aInvalidClientI ; "Invalid client id, exceed the maximum..." 25729 mov edi, 3 ; LOG_ERR 2572e xor eax, eax 25730 call _syslog 25735 jmp loc_256F6 ; return 0 ``` **要点**: `0x2E6D6F63` 按小端序分解为 `63 6F 6D 2E` = `c o m .`。验证逻辑仅检查前 4 字节与长度, 不做任何 ACL 或命名空间隔离。 ### 3.3 RpcClient::Call(service, method, data) @ 0x25BF0 核心调用入口。验证服务名后委托 `Caller::Call`, 然后根据 `ServiceType` 创建对应的 `Response` 对象。 ```asm ; trimrpc::RpcClient::Call(const string& service, const string& method, const string& data) ; rdi = this, rsi = &service, rdx = &method, rcx = &data _ZN7trimrpc9RpcClient4CallERKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEES8_S8_: 25bf0 push r15 25bf2 push r14 25bf4 push r13 25bf6 mov r13, rcx ; r13 = &data 25bf9 push r12 25bfb mov r12, rsi ; r12 = &service 25bfe push rbp 25bff mov rbp, rdx ; rbp = &method 25c02 push rbx 25c03 mov rbx, rdi ; rbx = this (RpcClient*) 25c06 sub rsp, 48h ; 步骤 1: 验证服务名 25c0a call _ZN7trimrpc9RpcClient15VerifyServiceIdE... ; VerifyServiceId(service) 25c0f test al, al 25c11 jz loc_25E80 ; 验证失败 → 直接返回错误 ; 步骤 2: 调用 Caller::Call 25c17 mov rdi, [rbx] ; rdi = this->caller_ 25c1a mov rcx, r13 ; arg4 = data 25c1d mov rdx, rbp ; arg3 = method 25c20 mov rsi, r12 ; arg2 = service 25c23 lea r13, [rsp+78h+src+8] 25c28 call _ZN7trimrpc6Caller4CallE... ; Caller::Call(service, method, data) ; 步骤 3: 保存错误码, 拷贝响应字符串 25c31 mov [rbx+80h], eax ; this->last_call_error = Caller 返回值 25c37 mov rax, [rbx] ; rax = caller_ 25c3a mov rbp, [rax+50h] ; rbp = caller->response_data.size() 25c3e mov r14, [rax+48h] ; r14 = caller->response_data.data() ; ... 拷贝 caller->response_data 到 this->response_string (offset 0x50) ... ; 步骤 4: 获取 ServiceType → 创建 Response 25cd3 mov r13, cs:_ZN7trimrpc14RpcConnections9instance_E_ptr 25cda mov rbp, [r13+0] ; rbp = RpcConnections::instance_ 25ce2 test rax, rax 25ce5 jz loc_25EC8 ; 单例不存在 → DCL 创建 ; 拷贝 service_name, 调用 GetServiceType ; ... 25d32 call _ZN7trimrpc14RpcConnections14GetServiceTypeE... ; GetServiceType(service) 25d37 mov rdi, [rsp+78h+var_58] 25d3c mov ebp, eax ; ebp = service_type ; 步骤 5: Response::Create(type, response_string) 25d51 movzx esi, bp ; arg2 = service_type 25d54 mov rdx, r14 ; arg3 = &response_string 25d57 mov rdi, r12 ; arg1 = 输出 shared_ptr 25d5a call _ZN7trimrpc8Response6CreateE... ; Response::Create(type, data) ; 将 shared_ptr 移入 this->response (offset 0x70) 25d5f movdqa xmm0, xmmword ptr [rsp+78h+var_58] 25d65 mov rbp, [rbx+78h] ; 旧的 shared_ptr 控制块 25d69 pxor xmm1, xmm1 25d6d movaps xmmword ptr [rsp+78h+var_58], xmm1 ; 清零临时 shared_ptr 25d72 movups xmmword ptr [rbx+70h], xmm0 ; this->response = 新 shared_ptr ; ... 释放旧 shared_ptr 引用 ... ``` ### 3.4 RpcClient::Call(Request) @ 0x261E0 高层调用入口, 接受 `trimrpc::Request` 对象。 ```asm ; trimrpc::RpcClient::Call(const Request& req) ; rdi = this (RpcClient*), rsi = &req _ZN7trimrpc9RpcClient4CallERKNS_7RequestE: 261e0 push r14 261e2 mov r14, rdi ; r14 = this 261e5 push r13 261e7 push r12 261e9 push rbp 261ea push rbx 261eb mov rbx, rsi ; rbx = &req 261ee sub rsp, 60h ; 步骤 1: 序列化请求 261f2 mov rbp, rsp 261f5 mov rdi, rbp ; rdi = 栈上 string (返回值) 261f8 call _ZNK7trimrpc7Request5BuildB5cxx11Ev ; req.Build() → string ; 步骤 2: 检查 Build 结果 261fd cmp [rsp+var_s8], 0 ; Build 结果的 size == 0? 26203 jnz loc_26215 ; 非空 → 继续 ; Build 返回空串 → 检查 error_no 26205 mov rdi, rbx ; rdi = &req 26208 call _ZNK7trimrpc7Request8error_noEv ; req.error_no() 2620d test eax, eax 2620f jnz loc_262B0 ; error_no != 0 → 直接返回错误 ; 步骤 3: 提取 method 和 service 26215 lea r13, [rsp+var_s40] 2621a mov rsi, rbx 2621d mov rdi, r13 26220 call _ZNK7trimrpc7Request6methodB5cxx11Ev ; req.method() → string 26225 lea r12, [rsp+var_s20] 2622a mov rsi, rbx 2622d mov rdi, r12 26230 call _ZNK7trimrpc7Request7serviceB5cxx11Ev ; req.service() → string ; 步骤 4: 委托给 Call(service, method, data) 26235 mov rcx, rbp ; data (Build 输出) 26238 mov rdx, r13 ; method 2623b mov rsi, r12 ; service 2623e mov rdi, r14 ; this 26241 call _ZN7trimrpc9RpcClient4CallE... ; Call(service, method, data) 26246 mov rdi, [rsp+var_s20] 2624b mov ebx, eax ; 保存返回值 ; 清理三个临时 string ; ... operator delete × 3 ... 2629e add rsp, 60h 262a2 mov eax, ebx ; return error_code 262ac retn ``` --- ## 4. Caller — 内部调用核心 ### 4.1 Caller::NotifyResult() 回调 @ 0x12860 当 `ne*` 网络引擎接收到响应数据后, 通过此回调唤醒等待中的 `CallOutInSync`。 ```asm ; trimrpc::Caller::NotifyResult(ulong session, int error_code, const string& data) ; rdi = this (Caller*), rsi = session_id, edx = error_code, rcx = &data _ZN7trimrpc6Caller12NotifyResultEmiRKNSt7__cxx1112basic_stringIcSt11char_traitsIcESaIcEEE: 12860 push r15 12862 push r14 12864 lea r14, [rdi+78h] ; r14 = &this->mutex (offset 0x78) 12868 push r13 1286a mov r13d, edx ; r13d = error_code 1286d push r12 1286f push rbp 12870 mov rbp, rcx ; rbp = &data (响应字符串) 12873 push rbx 12874 mov rbx, rdi ; rbx = this ; 加锁 12877 mov rdi, r14 ; mutex 1287a sub rsp, 18h 1287e call _pthread_mutex_lock 12883 test eax, eax 12885 jnz loc_12996 ; 加锁失败 → throw system_error ; 拷贝响应数据到 this->response_data (offset 0x48) 1288b lea rax, [rbx+48h] ; rax = &this->response_data 1288f cmp rbp, rax ; 自赋值检查 12892 jz loc_128C4 ; 是同一个对象 → 跳过拷贝 ; std::string 赋值操作 (SSO-aware) 12894 mov rax, [rbx+48h] ; rax = response_data.ptr 12898 lea r15, [rbx+58h] ; r15 = response_data 内联缓冲区 1289c mov r12, [rbp+8] ; r12 = data.size() 128a0 cmp rax, r15 ; 当前是否使用堆内存? 128a3 jz loc_12978 ; 使用 SSO → 容量固定 15 128a9 mov rdx, [rbx+58h] ; rdx = 当前容量 128ad cmp rdx, r12 ; 容量 >= 新数据大小? 128b0 jb loc_128F0 ; 容量不足 → 重新分配 ; 容量足够, 直接 memcpy 128b2 test r12, r12 128b5 jnz loc_12945 ; size > 0 → memcpy ; size == 0 → 直接截断 128bb mov [rbx+50h], r12 ; response_data.size = 0 128bf mov byte ptr [rax+r12], 0 ; '\0' 终止 ; 设置 error_code 并唤醒等待线程 128c4 mov [rbx+68h], r13d ; this->error_code = error_code (offset 0x68) 128c8 lea rdi, [rbx+0A0h] ; rdi = &this->cond_var (offset 0xA0) 128cf call std::condition_variable::notify_one() ; 唤醒 CallOutInSync ; 解锁 128d4 add rsp, 18h 128d8 mov rdi, r14 ; mutex 128db pop rbx ; ... 寄存器恢复 ... 128e5 jmp _pthread_mutex_unlock ; 尾调用 unlock ; memcpy 路径 (size == 1 快速路径) 12945 mov rsi, [rbp+0] ; src = data.data() 12949 cmp r12, 1 1294d jz loc_12988 ; 单字节 1294f mov rdx, r12 ; n 12952 mov rdi, rax ; dest 12955 call _memcpy 1295a mov rax, [rbx+48h] 1295e jmp loc_128BB ; 设 size 并 '\0' 12988 movzx edx, byte ptr [rsi] ; 读取单字节 1298b mov [rax], dl ; 写入 1298d mov rax, [rbx+48h] 12991 jmp loc_128BB ``` ### 4.2 Caller::BuildTrimSrvPacket() @ 0x12D20 构建 TrimSrv 协议请求包。 ```asm ; trimrpc::Caller::BuildTrimSrvPacket(const string& service, const string& method) ; 返回: shared_ptr 通过 rdi (隐式返回值指针) ; rdi = 返回值指针, rsi = this (Caller*), rdx = &service, rcx = &method _ZN7trimrpc6Caller18BuildTrimSrvPacketE...: 12d20 push r15 12d22 push r14 12d24 push r13 12d26 mov r13, rsi ; r13 = this (Caller*) 12d29 push r12 12d2b mov r12, rdi ; r12 = 返回值指针 12d2e mov rdi, rcx ; rdi = &method 12d31 push rbp 12d32 mov rbp, rdx ; rbp = &service (未使用, TrimSrv 不需要) 12d35 push rbx 12d36 sub rsp, 8 ; 步骤 1: 查询命令 ID 12d3a call TrimSrvCommand::GetCommandId ; GetCommandId(method) 12d3f mov edi, 48h ; 分配 0x48 = 72 字节 12d44 mov r14d, eax ; r14d = command_id 12d47 call __Znwm ; operator new(72) ; 步骤 2: 初始化 TrimSrvRequestPacket (控制块 + 对象) 12d4c mov r15, [rbp+0] ; r15 = service.data() (保存备用) 12d50 mov rbp, [rbp+8] ; rbp = service.size() 12d54 mov rbx, rax ; rbx = 新分配的内存 ; 设置 shared_ptr 控制块 12d57 mov rax, cs:qword_29990 ; 初始引用计数 = {1, 1} 12d5e lea rdi, [rbx+28h] ; 内联缓冲区地址 12d62 mov [rbx+8], rax ; control_block.ref_counts = {1, 1} 12d66 mov rax, cs:counted_ptr_inplace_vtable 12d6d mov [rbx+18h], rdi ; data.ptr = &inline_buf 12d71 add rax, 10h 12d75 mov [rbx], rax ; control_block.vtable = counted_ptr_inplace_vtable 12d78 mov rax, cs:TrimSrvRequestPacket_vtable 12d7f add rax, 10h 12d83 mov [rbx+10h], rax ; packet.vtable = TrimSrvRequestPacket vtable ; 步骤 3: 拷贝 method 字符串到 packet.data 12d87 cmp rbp, 0Fh 12d8b ja loc_12E20 ; > 15 字节 → 堆分配 12d91 cmp rbp, 1 12d95 jnz loc_12E00 ; != 1 12d97 movzx eax, byte ptr [r15] ; 单字节拷贝 12d9b mov [rbx+28h], al ; 步骤 4: 设置其余字段 12d9e mov [rbx+20h], rbp ; data.size 12da2 mov rdx, cs:Caller::session_id_ptr ; 全局 session_id 原子计数器 12da9 mov eax, 1 12dae mov byte ptr [rdi+rbp], 0 ; 追加 '\0' 12db2 lea rbp, [rbx+10h] ; rbp = packet 指针 12db6 mov qword ptr [rbx+38h], 0 ; packet.data_length = 0 12dbe mov [rbx+40h], r14d ; packet.command_id = command_id ; 步骤 5: 原子递增 session_id 12dc2 lock xadd [rdx], rax ; session_id_ = atomic_fetch_add(1) 12dc7 add rax, 1 ; rax = 新 session_id 12dcb mov rdi, rbp ; rdi = packet 12dce mov [r13+70h], rax ; caller->session_id = new_session_id 12dd2 mov rsi, [r13+70h] 12dd6 call TrimSrvRequestPacket::SetSession ; packet->SetSession(session_id) ; 步骤 6: 构造返回的 shared_ptr 12ddb mov [r12], rbp ; return.ptr = packet 12ddf mov rax, r12 12de2 mov [r12+8], rbx ; return.control_block = allocated memory 12de7 add rsp, 8 ; ... 寄存器恢复 ... 12df5 retn ``` ### 4.3 Caller::BuildRpcPacket() @ 0x14840 (部分) 构建 RpcPacket 协议请求包, 包含 JSON 序列化和原子 session_id 递增。 ```asm ; trimrpc::Caller::BuildRpcPacket(const string& service, const string& method, const string& data) ; rdi = 返回值指针, rsi = this, rdx = &service, rcx = &method, r8 = &data(token) _ZN7trimrpc6Caller14BuildRpcPacketE...: 14840 push r15 14842 mov r15, rsi ; r15 = this (Caller*) 14845 push r14 14847 push r13 14849 mov r13, rdx ; r13 = &service 1484c push r12 1484e mov r12, rcx ; r12 = &method 14851 push rbp 14852 mov rbp, r8 ; rbp = &data (token/extra) 14855 push rbx 14856 sub rsp, 118h ; 步骤 1: 分配 shared_ptr (0xB8 = 16 控制块 + 0xA8 对象) 14862 mov edi, 0B8h 14867 call __Znwm ; operator new(0xB8) 1486c mov rbx, rax ; rbx = 分配的内存 ; 步骤 2: 初始化控制块和 RpcPacket 1486f mov rax, cs:qword_29990 ; {1, 1} 引用计数 14876 mov [rbx+8], rax 1487a mov rax, cs:counted_ptr_inplace_vtable_RpcPacket 14881 add rax, 10h 14885 mov [rbx], rax ; control_block vtable 14888 lea rax, [rbx+10h] ; 对象起始地址 1488f mov [rsp+148h+var_130], rax 14894 call _ZN7trimrpc9RpcPacketC2Ev ; RpcPacket::RpcPacket() ; → magic=0x54525043, version=1 ; 步骤 3: 如果有 token, 拷贝到 packet->token 字段 14899 mov r14, [rbp+8] ; r14 = token.size() 1489d test r14, r14 148a0 jz loc_14990 ; 无 token → 跳到 JSON 构建 ; 拷贝 token 到 [rbx+0x78] (即 packet.token, offset 0x48 相对于 packet) ; ... SSO-aware string assign ... 14900 mov [rbx+2Ch], r14d ; 同时设置 reserved 字段 = token.size() ; 步骤 4: 构建 JSON 请求 ; (走 loc_14990 路径) 14990 lea r14, [rsp+148h+var_E0] 14998 call PPJson::MutDocument::MutDocument() ; 创建 JSON 文档 149a2 lea rax, [rsp+148h+var_88] 149b5 call PPJson::MutValue::MutValue(MutDocument&) ; 创建根节点 ; 连接 service + "." + method → "service.method" 字符串 ; 构建 JSON: {"data":{"req":".","pid":,"reqid":""}} ; ... 大量字符串拼接代码 ... ; 步骤 5: PPJson::MutDocument::write() → 序列化 JSON 字符串 ; 步骤 6: 设置为 packet.data 字段 ; 步骤 7: 原子递增 session_id 14949 mov rdx, cs:Caller::session_id_ptr 14950 mov eax, 1 14955 lock xadd [rdx], rax ; atomic_fetch_add(&session_id_, 1) 1495a mov rdx, [rsp+148h+var_130] 1495f add rax, 1 ; new_session_id = old + 1 14963 mov [r15+70h], rax ; caller->session_id = new_session_id 14967 mov rax, [r15+70h] 1496b mov [rbx+20h], rax ; packet->session_id = new_session_id ; 步骤 8: 构造返回 shared_ptr 1496f mov rax, [rsp+148h+var_140] 14974 mov [rax], rdx ; return.ptr = &packet 14977 mov [rax+8], rbx ; return.control_block = allocation 1497b add rsp, 118h 1498c retn ``` ### 4.4 Caller::Call() @ 0x15480 核心调用编排器: 获取连接 → 构建数据包 → 同步等待响应。 ```asm ; trimrpc::Caller::Call(const string& service, const string& method, const string& data) ; rdi = this, rsi = &service, rdx = &method, rcx = &data _ZN7trimrpc6Caller4CallE...: 15480 push r15 15482 push r14 15484 mov r14, rcx ; r14 = &data 15487 push r13 15489 mov r13, rsi ; r13 = &service 1548c push r12 1548e push rbp 1548f mov rbp, rdi ; rbp = this (Caller*) 15492 push rbx 15493 sub rsp, 58h ; 检查 response_data 是否为空 (旧数据清理) 15497 cmp qword ptr [rdi+50h], 0 ; this->response_data.size == 0? 1549c mov [rsp+88h+var_80], rdx 154a1 jnz loc_156E0 ; 非空 → 清理旧响应数据 ; 初始化 error_code = -1 154a7 mov dword ptr [rbp+68h], 0FFFFFFFFh ; this->error_code = -1 ; ---- 步骤 1: 获取/创建 RpcConnections 单例 ---- 154ae mov r12, cs:RpcConnections::instance_ptr 154b5 mov rbx, [r12] ; rbx = instance_ 154b9 mov rax, [r12] 154bd test rax, rax 154c0 jz loc_15730 ; 单例不存在 → DCL 创建 ; ---- 步骤 2: 建立连接 ---- 154c6 mov rsi, r13 ; &service 154c9 mov rdi, rbx ; RpcConnections* 154cc call RpcConnections::Connect ; Connect(service) 154d1 test eax, eax 154d3 jz loc_154E8 ; 0 = 成功 154d5 ; 非零 → 返回连接错误码 (3 或 4) add rsp, 58h ; ... retn ; ---- 步骤 3: 获取 ServiceType ---- 154e8 mov rbx, [r12] ; 重新加载 instance ; ... 拷贝 service 到栈上 string ... 15542 call RpcConnections::GetServiceType ; GetServiceType(service) → eax 15547 mov rdi, [rsp+88h+var_58] 1554c mov ebx, eax ; ebx = service_type (uint16_t) ; ---- 步骤 4: 根据类型构建数据包 ---- 1556a cmp bx, 1 ; service_type == 1? 1556e jz loc_15AA0 ; TrimSrv → BuildTrimSrvPacket ; AppCgi (type != 1) → BuildRpcPacket 15574 mov rcx, [rsp+88h+var_80] ; &method 15579 mov rdi, [rsp+88h+src] ; 返回值指针 1557e mov r8, r14 ; &data 15581 mov rdx, r13 ; &service 15584 mov rsi, rbp ; this 15587 call Caller::BuildRpcPacket ; BuildRpcPacket(service, method, data) ; ---- 步骤 5: CallOutInSync ---- ; ... shared_ptr 引用计数管理 ... 15636 lea rdx, [rsp+88h+var_68] ; &packet (shared_ptr) 1563b mov rsi, r13 ; &service 1563e mov rdi, rbp ; this 15641 call Caller::CallOutInSync ; CallOutInSync(service, packet) ; ---- 步骤 6: CancelByPacket 清理 ---- 15646 mov r13, [r12] 15657 mov rdi, qword ptr [rsp+88h+var_68] ; packet* 1565c mov rax, [rdi] 1565f call qword ptr [rax+18h] ; packet->session() 虚调用 15662 mov rsi, rax 15665 mov rdi, r13 15668 call RpcConnections::CancelByPacket ; CancelByPacket(session_id) ; 检查 TrimSrv 错误映射 1566d cmp bx, 1 ; 是 TrimSrv 协议? 15671 jz loc_15AC0 ; 是 → 检查 error 8 → 202 映射 ; 返回 error_code 1567c mov eax, [rbp+68h] ; eax = this->error_code ; ... 释放 shared_ptr ... 154d5 add rsp, 58h ; ... retn ; ---- DCL 单例创建路径 (loc_15730) ---- 15730 mov r15, cs:mutex ; 全局互斥锁 15737 mov rdi, r15 1573a call _pthread_mutex_lock ; ... 15758 mov edi, 168h ; sizeof(RpcConnections) = 360 字节 1575d call __Znwm ; operator new(360) ; ... 初始化 RB-tree 头节点 × 5 ... ; ... 调用 RpcConnections::Init(10, thread_count) ... ; ... 存储到全局 instance_ ... ; ... pthread_mutex_unlock ... ``` ### 4.5 Caller::CallOutInSync() @ 0x13CB0 (部分) 同步阻塞等待 RPC 响应, 使用 `pthread_cond_clockwait` 实现超时控制。 ```asm ; trimrpc::Caller::CallOutInSync(const string& service, shared_ptr& packet) ; rdi = this, rsi = &service, rdx = &packet _ZN7trimrpc6Caller13CallOutInSyncE...: 13cb0 push r15 13cb2 mov r15, rsi ; r15 = &service 13cb5 push r14 13cb7 lea r14, [rdi+78h] ; r14 = &this->mutex 13cbb push r13 13cbd mov r13, rdx ; r13 = &packet 13cc0 push r12 13cc2 push rbp 13cc3 mov rbp, rdi ; rbp = this (Caller*) 13cc9 push rbx 13cca sub rsp, 48h ; 加锁 13cce call _pthread_mutex_lock 13cd3 test eax, eax 13cd5 jnz loc_141FD ; 失败 → throw ; 获取连接句柄 13cdb mov r12, cs:RpcConnections::instance_ptr 13ce2 mov rbx, [r12] 13cf3 mov rsi, r15 ; &service 13cf6 mov rdi, rbx 13cf9 call RpcConnections::Get ; Get(service) → handle (rax) 13cfe mov rbx, [r12] 13d02 mov r15, rax ; r15 = connection_handle ; 构建回调: std::bind(&Caller::NotifyResult, this, _1, _2, _3) 13d16 mov edi, 18h ; 分配 24 字节给 bind 对象 13d32 call __Znwm 13d37 mov rdx, cs:Caller::NotifyResult_ptr ; 成员函数指针 13d44 mov qword ptr [rax+8], 0 ; padding 13d54 mov [rax+10h], rbp ; 捕获 this 指针 13d58 mov [rax], rdx ; 存储函数指针 ; 调用 RpcConnections::Send(handle, packet, callback) 13d94 lea rdx, [rsp+78h+var_68] ; &packet 13d99 mov rsi, r15 ; handle 13d9c mov rdi, rbx ; RpcConnections* 13d9f mov rcx, r12 ; callback 13da2 call RpcConnections::Send ; 计算超时绝对时间 13e05 mov r13d, [rbp+40h] ; r13d = this->timeout (秒, offset 0x40) 13e09 call steady_clock::now() ; 获取当前时间 13e0e lea rdi, [rbp+0A0h] ; rdi = &this->cond_var 13e1b imul r13, 3B9ACA00h ; timeout * 10^9 (纳秒) 13e22 add r13, rax ; deadline = now + timeout_ns ; ... 转换为 timespec {秒, 纳秒} ... ; 等待条件变量 (带超时) 13e52 mov edx, 1 ; CLOCK_MONOTONIC = 1 13e5c call _pthread_cond_clockwait ; 阻塞等待 ; 检查是否超时 13e61 call steady_clock::now() 13e66 cmp r13, rax ; deadline <= now ? 13e69 jle loc_13E88 ; 是 → 超时 ; 未超时 → 正常返回 13e6b add rsp, 48h 13e6f mov rdi, r14 ; mutex 13e7c jmp _pthread_mutex_unlock ; 尾调用 unlock + return ; 超时处理 13e88 mov dword ptr [rbp+68h], 64h ; this->error_code = 100 (0x64) 13e8f jmp loc_13E6B ; 解锁并返回 ``` --- ## 5. RpcConnections — 单例连接管理器 ### 5.1 Init() @ 0x196B0 ```asm ; trimrpc::RpcConnections::Init(uint max_connections, uint thread_count) ; rdi = this, esi = max_connections, edx = thread_count _ZN7trimrpc14RpcConnections4InitEjj: 196b0 cmp qword ptr [rdi], 0 ; ne_instance 已初始化? 196b4 jz loc_196C0 196b6 xor eax, eax ; 已初始化 → return 0 196b8 retn 196c0 push r12 196c2 mov r12d, esi ; r12d = max_connections 196c5 push rbp 196c6 mov ebp, edx ; ebp = thread_count 196c8 push rbx 196c9 mov rbx, rdi ; rbx = this ; 查询 CPU 核心数 196cc call std::thread::hardware_concurrency() 196d1 mov edi, r12d ; max_connections 196d4 cmp eax, 2 196d7 mov eax, 1 196dc cmovbe ebp, eax ; if (cores <= 2) thread_count = 1 ; 创建网络引擎实例 196df mov esi, ebp 196e1 call neCreateInstance ; neCreateInstance(max_conn, threads) 196e6 mov rdx, cs:OnConnected_ptr 196ed mov esi, 1 ; event_type = 1 (OnConnected) 196f2 mov [rbx], rax ; this->ne_instance = result 196f5 mov rdi, rax 196f8 call neSetCallback ; neSetCallback(inst, 1, OnConnected) 196fd mov rdi, [rbx] 19700 mov esi, 4 ; event_type = 4 (OnConnectError) 19705 mov rdx, cs:OnConnectError_ptr 1970c call neSetCallback 19711 mov rdi, [rbx] 19714 mov esi, 2 ; event_type = 2 (OnReceived) 19719 mov rdx, cs:OnReceived_ptr 19720 call neSetCallback 19725 mov rdi, [rbx] 19728 mov esi, 5 ; event_type = 5 (OnSendError) 1972d mov rdx, cs:OnSendError_ptr 19734 call neSetCallback 19739 mov rdi, [rbx] 1973c mov esi, 3 ; event_type = 3 (OnDisconnected) 19741 mov rdx, cs:OnDisconnected_ptr 19748 call neSetCallback ; 注册默认服务 1974d mov rdi, rbx 19750 call RpcConnections::InitServiceMap() 19755 pop rbx 19756 xor eax, eax ; return 0 1975b retn ``` ### 5.2 InitServiceMap() @ 0x19520 预注册 RPC Broker 为默认已知服务。 ```asm ; trimrpc::RpcConnections::InitServiceMap(void) ; rdi = this _ZN7trimrpc14RpcConnections14InitServiceMapEv: 19520 push r15 19522 xor eax, eax ; ... 栈帧设置 ... ; 构造 ServiceInfo 结构体 ; name 字段: "Broker" (6 字节) 195a0 mov dword ptr [rsp+var_B8], 6B6F7242h ; "Brok" (LE) 195a8 mov word ptr [rsp+var_B8+4], 7265h ; "er" 195ad mov [rsp+var_C0], 6 ; name.size = 6 195b6 mov [rsp+var_B8+6], 0 ; '\0' 终止 ; UDS 路径: 从 .rodata 中加载 (通过 xmmword 批量复制) 195bb call __Znwm ; 分配 0x1F 字节给路径字符串 195c0 movdqa xmm0, cs:xmmword_29B90 ; 加载 UDS 路径前 16 字节 195c8 mov rdi, [rsp+var_A8] 195cd movups [rax], xmm0 ; 写入前 16 字节 195d0 movdqa xmm0, cs:xmmword_29BA0 ; 加载后续字节 195d8 movups [rax+0Bh], xmm0 ; 写入 (有重叠) ; 插入到 service_map (RB-tree), key = "com.trim.rpcbroker" 19611 lea rsi, aComTrimRpcbrok ; "com.trim.rpcbroker" (18 字节) 19618 mov [rsp+var_A8], rax 19622 call std::_Rb_tree::_M_emplace_unique ; 插入到 [this+0x90] 的 RB-tree ; 清理临时字符串 (逆序释放) ; ... operator delete × 4 ... 1968a add rsp, 0A8h 1969b retn ``` ### 5.3 Send() @ 0x192D0 发送数据包, 包含回调注册、序列化和单次重试逻辑。 ```asm ; trimrpc::RpcConnections::Send(ulong handle, shared_ptr packet, ; function<void(ulong,int,string)> callback) ; rdi = this, rsi = handle, rdx = &packet, rcx = &callback _ZN7trimrpc14RpcConnections4SendE...: 192d0 push r15 192d2 lea r15, [rdi+118h] ; r15 = &this->send_mutex (offset 0x118) 192d9 push r14 192db push r13 192dd mov r13, rcx ; r13 = &callback 192e0 push r12 192e2 mov r12, rdx ; r12 = &packet 192e5 push rbp 192e6 mov rbp, rsi ; rbp = handle 192e9 push rbx 192ea mov rbx, rdi ; rbx = this 192ed mov rdi, r15 ; 加锁 send_mutex 192f4 call _pthread_mutex_lock 192f9 test eax, eax 192fb jnz loc_194EC ; 失败 → throw ; 加锁 callback_mutex (offset 0x140) 19301 lea r14, [rbx+140h] 19308 mov rdi, r14 1930b call _pthread_mutex_lock 19310 test eax, eax 19312 jnz loc_194E5 ; 获取 packet 的 session_id (通过虚函数) 19318 mov rdi, [r12] ; rdi = packet.get() 1931c mov rax, [rdi] ; rax = vtable 1931f call qword ptr [rax+18h] ; packet->session() → rax 19322 mov rsi, [rbx+0D0h] ; RB-tree root 19329 mov [rsp+var_58], rax ; 保存 session_id ; 在 callback RB-tree 中查找/插入 ; (RB-tree at offset 0xC0, 按 session_id 索引) ; ... 红黑树遍历代码 ... 194a8 lea rdx, [rsp+var_58] ; session_id 194ad lea rdi, [rbx+0C0h] ; &callback_tree 194b4 mov rcx, r13 ; &callback 194b7 call _Rb_tree::_M_emplace_hint_unique ; 插入 {session_id, callback} ; 解锁 callback_mutex 19396 mov rdi, r14 19399 call _pthread_mutex_unlock ; 序列化数据包 1939e mov rsi, [r12] ; packet.get() 193a2 lea rdi, [rsp+var_78] ; 栈上 string (输出) 193a7 mov rax, [rsi] ; vtable 193aa call qword ptr [rax+10h] ; packet->Serialize() → string ; 第一次 SendPacket 193ad mov ecx, [rsp+var_70] ; len = serialized.size() (uint32) 193b1 mov rdx, [rsp+var_78] ; data = serialized.data() 193b6 mov rsi, rbp ; handle 193b9 mov rdi, rbx ; this 193bc call RpcConnections::SendPacket 193c1 mov rdx, [rsp+var_78] 193c6 test al, al 193c8 jnz loc_194C8 ; 成功 → 清理并退出 ; 重试一次 (第二次 SendPacket) 193ce mov ecx, [rsp+var_70] 193d2 mov rsi, rbp 193d5 mov rdi, rbx 193d8 call RpcConnections::SendPacket 193dd mov rdx, [rsp+var_78] 193e2 test al, al 193e4 jnz loc_194C8 ; 成功 → 清理 ; 完全失败: syslog + callback(session, 102, "") 193ea mov rdx, rbp ; handle 193ed lea rsi, aSendErrorSessi ; "send error:session:0x%0lx" 193f4 mov edi, 3 ; LOG_ERR 193fb call _syslog ; 构造空字符串, 设 error_code = 102 (0x66) 19400 mov rdi, [r12] 1941c mov rax, [rdi] 1941f call qword ptr [rax+18h] ; packet->session() 19427 mov [rsp+var_80], rax ; session_id 1942c mov [rsp+var_84], 66h ; error_code = 102 ; ... 调用 callback(session, 102, "") ... ; 解锁 send_mutex 19489 mov rdi, r15 19496 jmp _pthread_mutex_unlock ; 尾调用 ``` ### 5.4 OnReceived() 回调 @ 0x17490 (部分) ```asm ; trimrpc::RpcConnections::OnReceived(ulong handle, char* data, ulong len, void* ctx) ; (ne* 网络引擎回调, 静态函数) _ZN7trimrpc14RpcConnections10OnReceivedE...: 17490 push r15 17492 lea rax, [rdi+0C8h] ; rax = &callback_tree_end (哨兵节点) 17499 push r14 ; ... 保存寄存器 ... ; ctx (r8) 是 PacketProcessor 指针 174a9 test r8, r8 174ac jz loc_17615 ; ctx == null → 返回 174b2 mov rbp, r8 ; rbp = PacketProcessor* 174b5 mov rsi, rdx ; data 174bb mov rdx, rcx ; len 174be mov rax, [rbp+0] ; vtable 174c2 mov rdi, rbp 174c5 call qword ptr [rax] ; PacketProcessor::AppendAndParse(data, len) 174c7 test al, al ; 解析出完整包? 174c9 jz loc_17615 ; 否 → 等待更多数据 ; 成功解析出完整包 174d0 mov rax, [rbp+0] ; vtable 174d9 mov rsi, rbp 174dc call qword ptr [rax+8] ; PacketProcessor::GetPacket() → shared_ptr 174df mov rsi, [rsp+var_88] ; packet* 174e9 mov rax, [rsi] 174ec call qword ptr [rax+20h] ; packet->data() → string ; 锁定 callback_mutex, 在 RB-tree 中按 session_id 查找回调 174ef lea r13, [r14+140h] 174f9 call _pthread_mutex_lock 17506 mov rdi, [rsp+var_88] 1750b mov rax, [rdi] 1750e call qword ptr [rax+18h] ; packet->session() → session_id ; RB-tree 查找 (session_id → callback) 17511 mov rbx, [r14+0D0h] ; rbx = tree root 17518 test rbx, rbx 1751b jz loc_17573 ; 树空 → 跳过 ; ... 标准 RB-tree 中序查找 ... ; 找到后调用 callback(session_id, error_code, data_string) ``` ### 5.5 AddService() @ 0x1A0E0 ```asm ; trimrpc::RpcConnections::AddService(string service_name, const ServiceInfo& info) ; rdi = this, rsi = &service_name, rdx = &info _ZN7trimrpc14RpcConnections10AddServiceE...: 1a0e0 push r15 1a0e2 lea rax, [rdi+0F0h] ; rax = &this->service_mutex (offset 0xF0) ; ... 保存寄存器 ... 1a0fd call _pthread_mutex_lock ; 加锁 service_mutex ; 在 service_map RB-tree (offset 0x90) 中查找 1a119 mov rax, [rsp+var_60] 1a11e mov rbp, [rax+0A0h] ; rbp = tree root (offset 0x90+0x10) 1a125 lea rbx, [rax+98h] ; rbx = tree end 哨兵 1a131 test rbp, rbp 1a134 jz loc_1A210 ; 空树 → 直接插入 ; RB-tree 查找 (按 string key 比较) 1a13a mov rax, [rsp+var_58] 1a145 mov r12, [rax] ; r12 = search_key.data() 1a148 mov r14, [rax+8] ; r14 = search_key.size() 1a159 mov r15, [rbp+28h] ; r15 = node->key.size() 1a160 mov rdi, [rbp+20h] ; node->key.data() 1a164 cmp r15, r14 1a167 cmovbe rdx, r15 ; min(node_len, search_len) 1a170 mov rsi, r12 ; search_key.data() 1a173 call _memcmp ; memcmp(node_key, search_key, min_len) ; ... 标准 BST 比较与左/右子树遍历 ... ; 找到匹配 → 不插入, 直接返回 (解锁) 1a1f1 mov rdi, [rsp+mutex] 1a204 jmp _pthread_mutex_unlock ; 未找到 → 插入新节点 1a210 mov rdi, [rsp+var_60] 1a215 mov rcx, [rsp+var_40] ; &info 1a21d mov rdx, [rsp+var_58] ; &service_name 1a222 add rdi, 90h ; &service_map (RB-tree) 1a229 call _Rb_tree::_M_emplace_hint_unique ; 插入 {name, info} 1a22e jmp loc_1A1F1 ; 解锁 ``` --- ## 6. PacketProcessor — 数据流解析器 ### 6.1 RpcPacketProcessor::AppendAndParse() @ 0x1CEC0 (部分) 处理 RpcPacket 协议的流式解析: 缓冲数据 → 检查 30 字节头 → 等待所有数据段。 ```asm ; trimrpc::RpcPacketProcessor::AppendAndParse(char* data, ulong len) ; rdi = this, rsi = data, rdx = len ; this->state (offset 0x10): 0=等待头部, 1=等待数据 ; this->packet (offset 0x1A0): shared_ptr _ZN7trimrpc18RpcPacketProcessor14AppendAndParseEPcm: 1cec0 push r14 1cec2 push r13 1cec4 lea r13, [rdi+28h] ; r13 = &this->ostream (std::ostream at offset 0x28) 1cecc mov rbx, rdi ; rbx = this ; 若有新数据, 先写入 ostream 缓冲区 1ced3 test rsi, rsi 1ced6 jz loc_1CEE4 1ced8 mov r12, rdx ; r12 = len 1cede jnz loc_1D080 ; 有数据 → ostream.write(data, len) ; 计算缓冲区中可用数据量 1cee4 lea r14, [rbx+18h] ; r14 = &this->istream (offset 0x18) 1cee8 mov rdi, r14 1ceeb call std::istream::tellg() ; 读位置 1cef0 mov rdi, r13 1cef3 mov r12, rax 1cef6 call std::ostream::tellp() ; 写位置 1cefb sub rax, r12 ; available = write_pos - read_pos 1cefe mov rbp, rax ; 根据状态分发 1cf01 mov eax, [rbx+10h] ; state 1cf04 test eax, eax 1cf06 jnz loc_1CFB0 ; state != 0 → 等待数据 ; ---- state 0: 等待 30 字节头部 ---- 1cf0c cmp rbp, 1Dh ; available > 29 ? 1cf10 jg loc_1CF28 1cf12 ; 数据不足 → 返回 false xor eax, eax retn ; 头部完整, 读取 30 字节 1cf28 mov edi, 1Fh 1cf37 call __Znwm ; 分配 31 字节缓冲区 1cf40 mov edx, 1Eh ; 读 30 字节 1cf48 mov rsi, rax ; buf 1cf4f movups [rax], xmm0 ; 清零缓冲区 1cf52 movups [rax+0Eh], xmm0 1cf56 mov [rsp+var_48], rax ; 保存 buf 指针 1cf6d call std::istream::read ; istream.read(buf, 30) ; 反序列化头部到 this->packet (RpcPacket 对象) 1cf72 mov rdi, [rbx+1A0h] ; rdi = this->packet (RpcPacket*) 1cf79 mov rsi, [rsp+var_48] ; buf 1cf7e mov edx, 1Eh 1cf83 call RpcPacket::DeserializeHeader ; 解析 30 字节头部 ; 切换到 state 1 (等待数据) 1cf8d mov dword ptr [rbx+10h], 1 ; this->state = 1 1cf94 sub rbp, 1Eh ; available -= 30 ; ---- state 1: 等待完整数据段 ---- 1cfb0 mov rdi, [rbx+1A0h] ; packet* 1cfb7 mov rax, [rdi] ; vtable 1cfba call qword ptr [rax+28h] ; packet->data_length() (虚调用) ; → 返回 data_len + token_len + extra_data_len + extra_data2_len 1cfbd mov eax, eax ; 零扩展 1cfbf cmp rax, rbp ; total_data_len > available ? 1cfc2 jg loc_1CF12 ; 是 → 返回 false, 等待更多数据 ; 所有数据就绪, 读取并反序列化 ; ... 读取 data_length 字节 ... ; ... 调用 RpcPacket::DeserializeData() ... ; ... 重置 state = 0 ... ; ... 返回 true ... ``` ### 6.2 TrimSrvPacketProcessor::AppendAndParse() @ 0x1D4B0 (部分) ```asm ; trimrpc::TrimSrvPacketProcessor::AppendAndParse(char* data, ulong len) _ZN7trimrpc22TrimSrvPacketProcessor14AppendAndParseEPcm: 1d4b0 push r15 1d4b8 lea r12, [rdi+28h] ; r12 = &ostream ; 有新数据时写入 ostream 1d4c5 test rsi, rsi 1d4c8 jz loc_1D4D2 1d4d0 jnz loc_1D510 ; → ostream.seekp(0, end); ostream.write(data, len) ; 计算可用数据量 1d4d2 lea rbp, [rbx+18h] ; &istream 1d4d9 call std::istream::tellg() 1d4e4 call std::ostream::tellp() 1d4e9 sub rax, r13 ; available 1d4ec mov r12, rax ; 需要至少 12 字节 (TrimSrv 头部大小) 1d4f1 cmp r12, 0Bh ; available > 11 ? 1d4f5 jg loc_1D538 1d4f7 ; 数据不足 → 返回 false xor eax, eax retn ; 读取 12 字节头部 1d538 lea r13, [rsp+var_88] 1d53d mov edx, 0Ch ; 12 字节 1d545 mov qword ptr [rsp+var_88], 0 ; 清零缓冲区 1d551 mov [rsp+var_98], r13 1d56c call std::istream::read ; istream.read(buf, 12) ; 调用 TrimSrvResponsePacket::DeserializeHeader 1d571 mov rdi, [rbx+1A0h] ; this->packet (TrimSrvResponsePacket*) 1d578 mov rsi, [rsp+var_98] ; buf 1d57d mov edx, 0Ch 1d582 call TrimSrvResponsePacket::DeserializeHeader ; 计算 data_length = packet_size - 12 1d587 mov rdi, [rbx+1A0h] 1d593 mov r14, cs:data_length_vfunc_ptr 1d5a6 cmp rax, r14 ; 虚函数指针比较 (内联优化检查) 1d5af mov rax, [rdi+10h] ; packet_size (从对象字段直接读) 1d5b3 sub eax, 0Ch ; data_length = packet_size - 12 1d5b6 lea r15, [rsp+var_68] 1d5bb mov r12d, eax ; r12d = data_length ; 分配缓冲区, 读取 data_length 字节 ; ... istream.read(buf, data_length) ... ; 调用 TrimSrvResponsePacket::DeserializeData(buf, data_length) ; 验证实际读取量 == 预期量 ; 成功后重置 stringstream 状态 ``` --- ## 7. TrimSrvCommand — 命令路由哈希表 ### 7.1 GetCommandId() @ 0x1FEF0 在全局命令映射表中查找方法名对应的命令 ID。支持两种查找策略: 小表线性遍历和大表布谷哈希。 ```asm ; trimrpc::TrimSrvCommand::GetCommandId(const string& method_name) ; rdi = &method_name ; 返回: eax = command_id (未找到返回 0) _ZN7trimrpc14TrimSrvCommand12GetCommandIdE...: 1fef0 push r15 1fef2 push r14 1fef4 push r13 1fef6 push r12 1fef8 mov r12, rdi ; r12 = &method_name 1fefc push rbx 1fefd sub rsp, 8 ; 加载全局 CommandMap 对象 1ff01 mov rbx, cs:obj ; rbx = &CommandMap (全局单例) 1ff08 cmp qword ptr [rbx+18h], 14h ; CommandMap.count > 20 ? 1ff0d ja loc_1FF88 ; 是 → 大表, 用布谷哈希 ; ======== 小表路径: 线性遍历链表 ======== 1ff0f mov rbx, [rbx+10h] ; rbx = 链表头节点 1ff13 test rbx, rbx 1ff16 jz loc_20011 ; 空表 → return 0 1ff1c mov rbp, [rdi+8] ; rbp = method_name.size() 1ff20 mov r13, [rdi] ; r13 = method_name.data() 1ff23 test rbp, rbp 1ff26 jz loc_1FF7C ; size==0 → 特殊处理 ; 链表遍历循环 1ff28 mov rsi, [rbx+8] ; node->name.data() 1ff2c cmp [rbx+10h], rbp ; node->name.size() == method_name.size() ? 1ff30 jz loc_1FF4E ; 长度匹配 → memcmp 1ff38 mov rbx, [rbx] ; rbx = node->next 1ff3b test rbx, rbx 1ff3e jz loc_20011 ; 链表结束 → return 0 1ff44 mov rsi, [rbx+8] 1ff48 cmp [rbx+10h], rbp 1ff4c jnz loc_1FF38 ; 长度不匹配 → 继续 ; 长度匹配, 做内容比较 1ff4e mov rdx, rbp ; n = size 1ff51 mov rdi, r13 ; s1 = method_name.data() 1ff54 call _memcmp 1ff59 test eax, eax 1ff5b jnz loc_1FF38 ; 不匹配 → 继续遍历 ; 找到匹配节点 → 返回 command_id 1ff5d mov eax, [rbx+28h] ; eax = node->command_id (offset 0x28) 1ff60 add rsp, 8 ; ... 寄存器恢复 ... 1ff6e retn ; ======== 大表路径: 布谷哈希 ======== 1ff88 mov rsi, [rdi+8] ; method_name.size() 1ff8c mov rdi, [rdi] ; method_name.data() 1ff8f mov edx, 0C70F6907h ; hash_seed = 0xC70F6907 1ff94 call std::_Hash_bytes ; hash(data, size, seed) 1ff99 mov rbp, [rbx+8] ; rbp = bucket_count 1ff9d xor edx, edx 1ff9f mov r13, rax ; r13 = hash_value 1ffa2 div rbp ; rdx = hash % bucket_count 1ffa5 mov rax, [rbx] ; rax = buckets 数组 1ffa8 mov r14, [rax+rdx*8] ; r14 = bucket[hash % count] (链表头) 1ffac mov r15, rdx ; r15 = bucket_index 1ffaf test r14, r14 1ffb2 jz loc_20011 ; 空桶 → return 0 ; 布谷哈希链遍历 1ffb4 mov rbx, [r14] ; rbx = 链表第一个节点 1ffb7 mov rcx, [rbx+30h] ; rcx = node->cached_hash 1ffdf cmp r13, rcx ; 哈希值匹配? 1ffe2 jnz loc_1FFC0 ; 不匹配 → 下一个节点 ; 哈希匹配, 做精确比较 1ffe4 mov rdx, [r12+8] ; method_name.size() 1ffe9 cmp rdx, [rbx+10h] ; == node->name.size() ? 1ffed jnz loc_1FFC0 1ffef test rdx, rdx 1fff2 jz loc_20005 ; 两者都是空串 → 匹配 1fff4 mov rsi, [rbx+8] ; node->name.data() 1fff8 mov rdi, [r12] ; method_name.data() 1fffc call _memcmp 20001 test eax, eax 20003 jnz loc_1FFC0 ; 不匹配 → 继续 ; 找到 → 取 command_id 20005 mov rbx, [r14] 20008 test rbx, rbx 2000b jnz loc_1FF5D ; 返回 node->command_id ; 未找到 20011 xor eax, eax ; return 0 20017 pop rbx ; ... 恢复 ... 20021 retn ``` **关键常量**: 布谷哈希种子 `0xC70F6907`, 通过 `std::_Hash_bytes` (MurmurHash 变体) 计算桶索引。 --- ## 8. Response 工厂 ### 8.1 Response::Create() @ 0x23B80 根据 `ServiceType` 分配不同大小的响应子类对象。 ```asm ; trimrpc::Response::Create(int service_type, const string& data) ; rdi = 输出 shared_ptr 指针, esi = service_type, rdx = &data _ZN7trimrpc8Response6CreateE...: 23b80 push r15 23b82 pxor xmm0, xmm0 23b86 push r14 23b8c mov r12, rdx ; r12 = &data 23b91 mov rbx, rdi ; rbx = 输出指针 23b98 movups [rdi], xmm0 ; 初始化 shared_ptr = {null, null} ; 分发 service_type 23b9b test esi, esi 23b9d jz loc_23C50 ; type == 0 → AppCgiResponse 23ba3 cmp esi, 1 23ba6 jz loc_23CF0 ; type == 1 → TrimSrvResponse ; ---- 默认: RawResponse (0x90 = 144 字节) ---- 23bac mov edi, 90h 23bb1 call __Znwm ; operator new(144) 23bb6 mov rbp, rax ; ... 设置 vtable, 引用计数 ... 23be8 mov rax, cs:RawResponse_vtable_ptr 23bef add rax, 10h 23bf3 mov [rbp+10h], rax ; packet.vtable = RawResponse vtable ; ... 拷贝 data 字符串, 初始化 PPJson ... 23c25 call PPJson::MutDocument::MutDocument() 23c2a lea rdi, [rbp+40h] 23c31 call PPJson::MutValue::MutValue(PPJson::MutDocument&) ; 设置返回的 shared_ptr 23c3a mov [rbx], r14 ; ptr = &packet 23c3d mov [rbx+8], rbp ; control_block = allocation ; ---- type 0: AppCgiResponse (0xE0 = 224 字节) ---- 23c50 mov edi, 0E0h 23c55 call __Znwm 23c5a mov rbp, rax 23c64 mov rsi, r12 ; &data 23c79 add rax, 10h 23c7d mov [rbp+0], rax ; counted_ptr vtable 23c81 call AppCgiResponse::AppCgiResponse(const string&) ; 构造函数 23c8a mov [rbx], r13 ; ptr 23c8d mov [rbx+8], rbp ; control_block ; ---- type 1: TrimSrvResponse (0xB0 = 176 字节) ---- ; (类似逻辑, 在 loc_23CF0) ``` --- ## 9. 常量与关键魔数速查 | 值 | 类型 | 语义 | 出现位置 | |-----|------|------|----------| | `0x54525043` | uint32 | RpcPacket magic "CPRT" (LE) | 0x1DDF7, DeserializeHeader | | `0x2E6D6F63` | uint32 | 服务名前缀 "com." (LE) | 0x25700 VerifyServiceId | | `0xC70F6907` | uint32 | 布谷哈希种子 | 0x1FF8F GetCommandId | | `0x1E` (30) | 头部大小 | RpcPacket 固定头长度 | Serialize/AppendAndParse | | `0x0C` (12) | 头部大小 | TrimSrv 固定头长度 | TrimSrvPacketProcessor | | `0x0A` (10) | 默认秒数 | RPC 调用超时 | Caller 构造函数 | | `0x80` (128) | 长度上限 | 服务名最大字节数 | VerifyServiceId | | `0x64` (100) | 错误码 | 同步等待超时 | CallOutInSync 0x13E88 | | `0x66` (102) | 错误码 | 发送完全失败 | Send 0x1942C | | `0xD0` (208) | 对象大小 | sizeof(Caller) | RpcClient 构造函数 | | `0xA8` (168) | 对象大小 | sizeof(RpcPacket) | BuildRpcPacket 0x14862 | | `0xB8` (184) | 分配大小 | RpcPacket + 控制块 (16+168) | BuildRpcPacket | | `0x48` (72) | 分配大小 | TrimSrvRequestPacket + 控制块 | BuildTrimSrvPacket | | `0x168` (360) | 对象大小 | sizeof(RpcConnections) | Caller::Call DCL 路径 | | `0x1B0` (432) | 对象大小 | sizeof(PacketProcessor) | Connect | | `0x88` (136) | 对象大小 | sizeof(RpcClient) | 构造函数 | | `0xE0` (224) | 对象大小 | sizeof(AppCgiResponse) | Response::Create | | `0xB0` (176) | 对象大小 | sizeof(TrimSrvResponse) | Response::Create | | `0x90` (144) | 对象大小 | sizeof(RawResponse) | Response::Create | --- ## 10. 错误码映射表 | 码 | 十六进制 | 语义 | 产生函数 | 地址 | |----|---------|------|----------|------| | 0 | 0x00 | 成功 | 各处 | — | | 1 | 0x01 | HandleServiceInfo 服务数量不匹配 | HandleServiceInfo | 0x26330 | | 3 | 0x03 | neCreateSocket 失败 | RpcConnections::Connect | 0x1A920 | | 4 | 0x04 | neConnect 失败 / 轮询超时 | RpcConnections::Connect | 0x1A920 | | 5 | 0x05 | 服务名超过 128 字节 | VerifyServiceId | 0x25718 | | 6 | 0x06 | 服务名不以 "com." 开头 | VerifyServiceId | 0x256D9 | | -1 | 0xFFFFFFFF | 初始值 (调用前) | Caller::Call | 0x154A7 | | 100 | 0x64 | pthread_cond_clockwait 超时 | CallOutInSync | 0x13E88 | | 102 | 0x66 | SendPacket 两次重试均失败 | RpcConnections::Send | 0x1942C | | 200 | 0xC8 | HandleServiceInfo JSON 解析失败 | HandleServiceInfo | 0x26330 | | 201 | 0xC9 | TrimSrvRequest::Build 失败 | TrimSrvRequest::Build | 0x23510 | | 202 | 0xCA | TrimSrv error 8 重映射 | Caller::Call | 0x15480 | --- ## 11. 函数地址完整索引 ### RpcClient (API 层) | 地址 | 大小 | 函数签名 | |------|------|----------| | `0x259b0` | 0x23D | `RpcClient::RpcClient(const string& uuid, const string& service_name)` | | `0x256c0` | 0x77 | `RpcClient::VerifyServiceId(const string& service)` | | `0x25bf0` | 0x5F0 | `RpcClient::Call(const string& service, const string& method, const string& data)` | | `0x261e0` | 0xF4 | `RpcClient::Call(const Request& req)` | | `0x262e0` | 0x4F | `RpcClient::Call(const string& service, const string& method)` | | `0x25740` | 0x166 | `RpcClient::Call(const string&, const string&, const string&, function<>)` | | `0x27740` | 0x8FF | `RpcClient::ApplyPermission(const vector& services)` | | `0x28040` | 0x232 | `RpcClient::ApplyPermission(const string& service)` | | `0x26330` | 0x140A | `RpcClient::HandleServiceInfo(const string& response, uint count)` | | `0x258b0` | 0x4 | `RpcClient::SetTimeOut(uint seconds)` | | `0x258c0` | 0x7 | `RpcClient::error_no()` | | `0x25980` | 0x2E | `RpcClient::response()` | ### Caller (调用核心) | 地址 | 大小 | 函数签名 | |------|------|----------| | `0x12b60` | 0x111 | `Caller::Caller(const string& uuid)` | | `0x12c80` | 0x1A | `Caller::Caller(const string& uuid, uint timeout)` | | `0x15480` | 0x8F7 | `Caller::Call(const string& service, const string& method, const string& data)` | | `0x14840` | 0xC38 | `Caller::BuildRpcPacket(const string& service, const string& method, const string& data)` | | `0x12d20` | 0x159 | `Caller::BuildTrimSrvPacket(const string& service, const string& method)` | | `0x13cb0` | 0x58A | `Caller::CallOutInSync(const string& service, shared_ptr& packet)` | | `0x14240` | 0x5F8 | `Caller::CallOutWithFuture(const string& service, shared_ptr& packet)` | | `0x12860` | 0x151 | `Caller::NotifyResult(ulong session, int error, const string& data)` | | `0x12ca0` | 0x73 | `Caller::Wait()` | ### RpcConnections (连接管理) | 地址 | 大小 | 函数签名 | |------|------|----------| | `0x196b0` | 0xAC | `RpcConnections::Init(uint max_conn, uint threads)` | | `0x19520` | 0x17C | `RpcConnections::InitServiceMap()` | | `0x1a920` | ~0x700 | `RpcConnections::Connect(const string& service)` | | `0x192d0` | 0x244 | `RpcConnections::Send(ulong handle, shared_ptr, function<> callback)` | | `0x17490` | ~0x300 | `RpcConnections::OnReceived(ulong, char*, ulong, void*)` | | `0x1a0e0` | 0x15C | `RpcConnections::AddService(string name, const ServiceInfo& info)` | | `0x1a730` | — | `RpcConnections::GetServiceType(string service)` | | `0x17440` | 0x50 | `RpcConnections::SendPacket(ulong handle, const char* data, uint len)` | ### 协议层 | 地址 | 大小 | 函数签名 | |------|------|----------| | `0x1ddf0` | 0x9B | `RpcPacket::RpcPacket()` | | `0x1dc30` | 0x1BC | `RpcPacket::Serialize() → string` | | `0x1dea0` | 0x38 | `RpcPacket::DeserializeHeader(const char*, uint)` | | `0x1dee0` | 0x670 | `RpcPacket::DeserializeData(const char*, uint)` | | `0x163e0` | 0xA9 | `TrimSrvRequestPacket::Serialize() → string` | | `0x21320` | 0x17C | `TrimSrvResponsePacket::DeserializeHeader(const char*, uint)` | | `0x1cec0` | ~0x2C0 | `RpcPacketProcessor::AppendAndParse(char*, ulong)` | | `0x1d4b0` | ~0x2C0 | `TrimSrvPacketProcessor::AppendAndParse(char*, ulong)` | | `0x1fef0` | 0x132 | `TrimSrvCommand::GetCommandId(const string&)` | ### Request/Response | 地址 | 大小 | 函数签名 | |------|------|----------| | `0x21680` | — | `Request::Request(const string& service, const string& method, const string& extra)` | | `0x23510` | — | `TrimSrvRequest::Build()` | | `0x21c70` | — | `AppCgiRequest::Build()` | | `0x23b80` | ~0x230 | `Response::Create(int type, const string& data) → shared_ptr` | | `0x24c20` | — | `AppCgiResponse::error_no()` | | `0x252f0` | — | `TrimSrvResponse::error_no()` | --- </details>