Rust in Everything
基于 tauri 2.12.5

IPC

这一部分把调用主线拆开逐段讲。一次 invoke 已经把 invoke('greet', { name }) 从头到尾串了一遍,抓到了两侧的请求头和线程;这里每一页只管其中一段,讲到边界条件为止:什么样的参数会被编成什么样的正文,什么样的请求会被拒、拒的时候回什么,什么情况下会悄悄换一条路、换路的代价是多少。

源码集中在 crates/tauri/scripts/ 下的四个 JS 文件(core.js、ipc.js、ipc-protocol.js、process-ipc-message-fn.js)和 crates/tauri/src/ipc/(protocol.rs、mod.rs、command.rs、channel.rs、format_callback.rs),加上 webview/mod.rs 里的 Webview::on_message。

  1. JS 侧:invoke 与回调:回调存在 core.js 闭包里的一个 Map 中,成对登记、用一次就删;processIpcMessage 按参数类型选 JSON 或原始字节,顶层的普通数组会被当成字节发出去;fetch 链上任何一步失败都会让页面永久改用 postMessage,并把这条消息重发一遍。
  2. ipc 协议:ipc:// 协议处理函数和 postMessage 处理函数各自把请求拼成 InvokeRequest;parse_invoke_request 按固定顺序检查路径、Content-Type、四个头和正文,哪一步不过就回 500 和一行纯文本;应答在协议路上是 HTTP 响应,在 postMessage 路上是一段 eval,5 MiB 字节在后者上慢几十倍。
  3. on_message 与分发:Webview::on_message 先比对 invoke key,再按来源判断本地还是远程、在需要时查 ACL,最后按 plugin: 前缀分给插件或 generate_handler! 生成的处理函数,找不到时回 Command ... not found。
  4. 参数、返回值与 InvokeResolver:CommandArg 和 CommandItem 怎么把 InvokeBody 反序列化成命令参数,返回值怎么变成 InvokeResponseBody,错误怎么变成 InvokeError,同步和 async 命令各在哪个线程上应答。
  5. Channel:ipc::Channel 借用一个回调 id 让 Rust 反复往页面推消息,小消息直接 eval,大消息先存起来再由页面用 fetch 取回;和事件比较吞吐。
flowchart TB
  jsinv["invoke 登记回调"] --> enc["processIpcMessage 编码"]
  enc --> fetchp["fetch ipc 协议"]
  enc --> pm["window.ipc.postMessage"]
  fetchp --> parse["parse_invoke_request"]
  pm --> hipc["handle_ipc_message"]
  parse --> onmsg["on_message 分发"]
  hipc --> onmsg
  onmsg --> args["命令参数与 Resolver"]
  args --> resp["HTTP 响应或 eval"]
  resp --> runcb["runCallback 兑现"]
  args --> chan["Channel 推送"]
  chan --> runcb

前两页在 on_message 之前,管「消息怎么来、应答怎么回去」;后三页在 on_message 之后,管「消息交给谁、怎么变成函数调用、结果怎么送回来」。ACL 在 on_message 里被调用,但规则本身怎么解析、怎么匹配在 07 安全;#[command] 宏展开成什么在 #[command] 与 generate_handler!;ipc:// 请求在 WebKit 和 wry 里怎么流转在 wry 在 macOS 上。

💬 关联讨论

发起讨论
加载中...