<?xml version="1.0" encoding="utf-8" standalone="yes"?>
<rss version="2.0" xmlns:atom="http://www.w3.org/2005/Atom">
  <channel>
    <title>洋葱网络 on 方糖猫Blog</title>
    <link>https://ftmao.com/blog/tags/%E6%B4%8B%E8%91%B1%E7%BD%91%E7%BB%9C/</link>
    <description>AI 视频、AI Agent 与工程实践笔记</description>
    <generator>Hugo</generator>
    <language>zh-CN</language>
    <lastBuildDate>Tue, 19 Dec 2017 10:00:00 +0800</lastBuildDate><atom:link href="https://ftmao.com/blog/tags/%E6%B4%8B%E8%91%B1%E7%BD%91%E7%BB%9C/index.xml" rel="self" type="application/rss+xml" /><item>
      <title>基于 UDP 通信的洋葱网络设计：服务器、Android 客户端与 P2P 控制总结</title>
      <link>https://ftmao.com/blog/posts/udp-onion-network-and-p2p-design/</link>
      <pubDate>Tue, 19 Dec 2017 10:00:00 +0800</pubDate>
      <guid>https://ftmao.com/blog/posts/udp-onion-network-and-p2p-design/</guid>
      <description>&lt;h1 id=&#34;基于-udp-通信的洋葱网络设计服务器android-客户端与-p2p-控制总结&#34;&gt;基于 UDP 通信的洋葱网络设计：服务器、Android 客户端与 P2P 控制总结&lt;/h1&gt;
&lt;blockquote&gt;
&lt;p&gt;最近要实现端到端的加密通信，现在把设计方案记录下来。目标读者：刚接触洋葱网络、P2P 或 UDP 信令设计，想了解「怎么用 UDP 搭一个匿名转发网络，再基于它做 P2P」的同学。&lt;br&gt;
阅读目标：建立整体架构图、理解关键模块职责、记住常见踩坑点。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;0-写在前面为什么用-udp-做洋葱网络--p2p-信令&#34;&gt;0. 写在前面：为什么用 UDP 做洋葱网络 + P2P 信令？&lt;/h2&gt;
&lt;p&gt;洋葱网络（Onion Network）的核心思想是：&lt;strong&gt;数据像剥洋葱一样，一层一层加密转发&lt;/strong&gt;。每一跳节点只知道上一跳和下一跳，不知道完整路径，从而保护通信双方的隐私。&lt;/p&gt;
&lt;p&gt;传统洋葱路由（如 Tor）多跑在 TCP 上，因为 TCP 可靠、有序。但在移动场景或 P2P 场景里，UDP 有几个独特优势：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;开销小&lt;/strong&gt;：UDP 头只有 8 字节，没有连接建立/拆除开销，适合移动弱网。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;NAT 友好&lt;/strong&gt;：UDP 是 NAT 打洞（Hole Punching）最常用的传输层协议，STUN/TURN/ICE 都优先用 UDP。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;适合信令&lt;/strong&gt;：P2P 建立连接前的「打招呼」消息很短，UDP 足够用；真正的大数据再切到 P2P 直连或中继。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;抗审查更灵活&lt;/strong&gt;：无连接特性让流量更像普通 DNS/QUIC/游戏报文，便于混淆。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;但 UDP 也带来问题：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;不保证可靠、不保证顺序，需要自己在应用层做重传、去重、拥塞控制。&lt;/li&gt;
&lt;li&gt;报文可能被分片、被防火墙限制、被运营商 QoS。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这篇文章会带你从「一个 UDP 报文怎么进洋葱网络」开始，逐步拆解：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;洋葱网络的层次结构与加密过程。&lt;/li&gt;
&lt;li&gt;服务器（目录服务 + 中继 + 信令服务）应该怎么设计。&lt;/li&gt;
&lt;li&gt;Android 客户端的结构与关键代码思路。&lt;/li&gt;
&lt;li&gt;怎么用这套 UDP 信令网络做 P2P 控制（NAT 穿透、打洞、直连、中继回退）。&lt;/li&gt;
&lt;li&gt;新人最容易踩的坑与规避方法。&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;h2 id=&#34;1-洋葱网络核心概念先建立知识地图&#34;&gt;1. 洋葱网络核心概念：先建立「知识地图」&lt;/h2&gt;
&lt;h3 id=&#34;11-洋葱路由-vs-普通代理的区别&#34;&gt;1.1 洋葱路由 vs 普通代理的区别&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;对比项&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;普通代理&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;洋葱网络&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;路径&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;单跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;多跳（通常 3 跳）&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;加密&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;端到端一次加密&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每跳一层加密&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;节点知情范围&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;代理知道你是谁、你访问哪&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每跳只知道上一跳和下一跳&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;代表&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HTTP/SOCKS 代理&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Tor、I2P、自定义洋葱网络&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;12-一次洋葱转发的过程&#34;&gt;1.2 一次洋葱转发的过程&lt;/h3&gt;
&lt;p&gt;假设 Alice 要给 Bob 发消息，路径为：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt; E[&amp;#34;入口节点 (Entry)&amp;#34;] --&amp;gt; M[&amp;#34;中间节点 (Middle)&amp;#34;] --&amp;gt; X[&amp;#34;出口节点 (Exit)&amp;#34;] --&amp;gt; B[Bob]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;Alice 会先把报文&lt;strong&gt;从后往前&lt;/strong&gt;逐层加密：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;用 Exit 的公钥加密「Exit → Bob 的真实数据」，得到 &lt;code&gt;C3&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用 Middle 的公钥加密「下一站是 Exit + C3」，得到 &lt;code&gt;C2&lt;/code&gt;。&lt;/li&gt;
&lt;li&gt;用 Entry 的公钥加密「下一站是 Middle + C2」，得到 &lt;code&gt;C1&lt;/code&gt;。&lt;/li&gt;
&lt;/ol&gt;
&lt;p&gt;然后 Alice 把 &lt;code&gt;C1&lt;/code&gt; 发给 Entry。Entry 用自己的私钥解密后，看到「下一站是 Middle」，把 &lt;code&gt;C2&lt;/code&gt; 转发给 Middle。Middle 解密后转发 &lt;code&gt;C3&lt;/code&gt; 给 Exit。Exit 解密后拿到真实数据，发给 Bob。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt;|C1| E[Entry&amp;lt;br/&amp;gt;解密: 下一站 Middle] --&amp;gt;|C2| M[Middle&amp;lt;br/&amp;gt;解密: 下一站 Exit] --&amp;gt;|C3| X[Exit&amp;lt;br/&amp;gt;解密: 真实数据] --&amp;gt;|明文| B[Bob]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人记忆点&lt;/strong&gt;：洋葱网络的数据是「加密好的俄罗斯套娃」，每到一个节点就剥掉一层外衣。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;13-udp-洋葱报文的基本结构&#34;&gt;1.3 UDP 洋葱报文的基本结构&lt;/h3&gt;
&lt;p&gt;一个 UDP 洋葱报文可以简单设计为：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|  版本 (1B)     |  命令 (1B)     |    跳数 (1B)   |   预留 (1B)    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                        会话 ID / 电路 ID (4B)                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+----------------+----------------+----------------+----------------+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         下一跳洋葱层数据 ...                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+------------------------------------------------------------------+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;ul&gt;
&lt;li&gt;&lt;strong&gt;版本&lt;/strong&gt;：协议版本，方便后续升级。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;命令&lt;/strong&gt;：路由、数据、建立、销毁、心跳等。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;跳数 / TTL&lt;/strong&gt;：防止报文无限循环，每经过一跳减 1。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;会话 ID / 电路 ID&lt;/strong&gt;：标识一条逻辑链路，让节点知道该用哪把密钥解密。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;下一跳洋葱层数据&lt;/strong&gt;：被当前节点私钥解密后，才能得到真正的下一跳地址和载荷。&lt;/li&gt;
&lt;/ul&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人容易混淆的点&lt;/strong&gt;：洋葱网络里的「加密」不是端到端 TLS，而是&lt;strong&gt;逐跳加密&lt;/strong&gt;。每一跳都能解密自己最外层，但看不到内层。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;2-系统整体架构&#34;&gt;2. 系统整体架构&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 公网服务层
        DS[Directory Server&amp;lt;br/&amp;gt;目录服务：发布可信节点]
        SS[Signaling Server&amp;lt;br/&amp;gt;信令服务：NAT 信息交换]
        RS[Relay Server&amp;lt;br/&amp;gt;中继服务：UDP 洋葱转发]
    end

    subgraph Android 客户端
        AC[App 业务层]
        CM[Connection Manager&amp;lt;br/&amp;gt;管理 Circuits / Peers]
        OC[Onion Core&amp;lt;br/&amp;gt;分层加密 / 解密]
        NC[Network Core&amp;lt;br/&amp;gt;UDP Socket / NAT / ICE]
    end

    subgraph 对端
        P2P[Peer B&amp;lt;br/&amp;gt;Android / Server / IoT]
    end

    AC --&amp;gt; CM
    CM --&amp;gt; OC
    CM --&amp;gt; NC
    OC --&amp;gt; NC
    NC --&amp;gt;|UDP| RS
    NC --&amp;gt;|UDP| SS
    NC -.-&amp;gt;|查询节点列表| DS
    NC -.-&amp;gt;|P2P 直连或中继| P2P&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;21-三类服务器职责&#34;&gt;2.1 三类服务器职责&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;服务&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;职责&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;协议&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Directory Server（目录服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;维护可信中继节点列表、公钥、负载、版本&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HTTPS / UDP 查询&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Signaling Server（信令服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;帮助两个 Peer 交换公网 IP:Port、NAT 类型、候选地址&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP + 可靠信令协议&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Relay Server（中继服务）&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;当 P2P 打洞失败时，转发 UDP 洋葱流量&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP 洋葱路由&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;实际部署中，目录服务可以用 HTTPS 更稳；信令和中继为了速度和 NAT 友好，优先用 UDP。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;3-服务器端设计&#34;&gt;3. 服务器端设计&lt;/h2&gt;
&lt;h3 id=&#34;31-目录服务directory-server&#34;&gt;3.1 目录服务（Directory Server）&lt;/h3&gt;
&lt;p&gt;目录服务就像「黄页」，让客户端知道哪些中继节点在线、可信、负载低。&lt;/p&gt;
&lt;h4 id=&#34;核心数据模型&#34;&gt;核心数据模型&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Node {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    node_id:        节点唯一 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    public_key:     Ed25519/X25519 公钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    endpoint:       公网 IP:UDP 端口
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bandwidth:      声明带宽（Mbps）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    load:           当前负载（连接数 / 带宽占用）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    version:        协议版本
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    flags:          入口 / 中间 / 出口 / 中继 标志位
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_seen:      最后一次心跳时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signature:      节点用私钥对以上字段签名
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;客户端获取节点列表的流程&#34;&gt;客户端获取节点列表的流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant C as Android Client
    participant D as Directory Server
    participant N1 as Node Entry
    participant N2 as Node Middle
    participant N3 as Node Exit

    C-&amp;gt;&amp;gt;D: HTTPS GET /nodes?count=3&amp;amp;flags=entry|middle|exit
    D--&amp;gt;&amp;gt;C: 返回节点列表 + 签名
    C-&amp;gt;&amp;gt;C: 验证签名、选择路径
    C-&amp;gt;&amp;gt;N1: UDP 建立 Circuit 请求
    N1--&amp;gt;&amp;gt;C: 返回 Circuit ID
    C-&amp;gt;&amp;gt;N1: 经过 N1 转发到 N2 建立
    N2--&amp;gt;&amp;gt;C: 返回确认
    C-&amp;gt;&amp;gt;N2: 经过 N1→N2 转发到 N3 建立
    N3--&amp;gt;&amp;gt;C: 返回确认&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：目录服务返回的节点列表必须带签名，客户端要校验，否则会被恶意节点钓鱼。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;32-中继服务relay-server&#34;&gt;3.2 中继服务（Relay Server）&lt;/h3&gt;
&lt;p&gt;中继服务是洋葱网络里的「快递员」：收到一个 UDP 报文，解密最外层，转发给下一跳。&lt;/p&gt;
&lt;h4 id=&#34;核心状态&#34;&gt;核心状态&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;Circuit {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    circuit_id:     本节点分配的 32 位 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    prev_node:      上一跳地址（IP:Port）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    next_node:      下一跳地址（IP:Port）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    shared_key:     与创建者协商的共享密钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    created_at:     创建时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    last_active:    最后活跃时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    bytes_forward:  转发字节数
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;转发流程&#34;&gt;转发流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[收到 UDP 报文] --&amp;gt; B{报文格式合法?}
    B --&amp;gt;|否| C[直接丢弃]
    B --&amp;gt;|是| D[查找 circuit_id]
    D --&amp;gt; E{存在?}
    E --&amp;gt;|否| C
    E --&amp;gt;|是| F[用 shared_key 解密洋葱层]
    F --&amp;gt; G{解密成功?}
    G --&amp;gt;|否| C
    G --&amp;gt;|是| H[读取下一跳地址 + 内层数据]
    H --&amp;gt; I[TTL - 1]
    I --&amp;gt; J{TTL &amp;gt; 0?}
    J --&amp;gt;|否| K[发送销毁通知]
    J --&amp;gt;|是| L[转发给下一跳]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h4 id=&#34;关键设计点&#34;&gt;关键设计点&lt;/h4&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;限速与防滥用&lt;/strong&gt;：单 Circuit 限制带宽，防止被当免费代理滥用。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;TTL 与环检测&lt;/strong&gt;：每跳 TTL 减 1，到 0 丢弃，防止恶意构造环路。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;密钥派生&lt;/strong&gt;：每个 Circuit 用 X25519 ECDH 协商出共享密钥，再用 HKDF 派出发送/接收两个方向的对称密钥。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;无日志策略&lt;/strong&gt;：中继节点不记录明文内容、不记录完整路径（实际上也看不到）。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;33-信令服务signaling-server&#34;&gt;3.3 信令服务（Signaling Server）&lt;/h3&gt;
&lt;p&gt;信令服务是 P2P 的「媒人」：两个 Peer 无法直接找到对方公网地址时，通过信令服务交换信息。&lt;/p&gt;
&lt;h4 id=&#34;数据模型&#34;&gt;数据模型&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;PeerInfo {
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    peer_id:        用户/设备唯一 ID
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    public_key:     用于加密信令的公钥
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    nat_type:       全锥型 / 限制型 / 端口限制型 / 对称型
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    local_candidates: [局域网 IP:Port, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    srflx_candidates: [STUN 映射后的公网 IP:Port, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    relay_candidates: [TURN 中继地址, ...]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    timestamp:      更新时间
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    signature:      防止伪造
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;}&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h4 id=&#34;典型信令流程&#34;&gt;典型信令流程&lt;/h4&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant S as Signaling Server
    participant B as Peer B

    A-&amp;gt;&amp;gt;S: 注册/上报 PeerInfo（UDP 心跳包）
    B-&amp;gt;&amp;gt;S: 注册/上报 PeerInfo

    A-&amp;gt;&amp;gt;S: 请求连接 peer_B
    S-&amp;gt;&amp;gt;S: 检查 A、B 是否在线
    S--&amp;gt;&amp;gt;A: 返回 B 的候选地址列表
    S--&amp;gt;&amp;gt;B: 转发 A 的连接请求 + A 的候选地址

    A-&amp;gt;&amp;gt;A: 按优先级尝试直连 / 打洞
    B-&amp;gt;&amp;gt;B: 同时尝试直连 / 打洞
    A--&amp;gt;&amp;gt;B: UDP 打洞成功，建立 P2P
    B--&amp;gt;&amp;gt;A: 业务数据互通

    alt 打洞失败
        A-&amp;gt;&amp;gt;S: 请求中继
        S--&amp;gt;&amp;gt;A: 分配 Relay 地址
        S--&amp;gt;&amp;gt;B: 通知 B 通过 Relay 通信
        A-&amp;gt;&amp;gt;R: 经中继与 B 通信
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：信令服务本身不传输业务数据，只传「怎么找到对方」。不要把它当成聊天服务器用。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;4-android-客户端设计&#34;&gt;4. Android 客户端设计&lt;/h2&gt;
&lt;p&gt;Android 客户端需要同时处理：业务逻辑、洋葱路由、UDP 网络、NAT 穿透、后台保活。建议按层拆分。&lt;/p&gt;
&lt;h3 id=&#34;41-客户端分层架构&#34;&gt;4.1 客户端分层架构&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    subgraph 应用层
        UI[UI / ViewModel]
        API[业务 API]
    end

    subgraph 网络中间层
        CM[ConnectionManager&amp;lt;br/&amp;gt;管理多 Circuit / 多 Peer]
        P2P[P2PController&amp;lt;br/&amp;gt;打洞 / 直连 / 中继决策]
        SM[SignalingClient&amp;lt;br/&amp;gt;与信令服务通信]
    end

    subgraph 核心引擎层
        OC[OnionCore&amp;lt;br/&amp;gt;加密 / 解密 / 报文封装]
        NC[NetworkCore&amp;lt;br/&amp;gt;UDP Socket / ICE / STUN]
        CR[CircuitStore&amp;lt;br/&amp;gt;持久化 Circuit 状态]
    end

    UI --&amp;gt; API
    API --&amp;gt; CM
    CM --&amp;gt; P2P
    CM --&amp;gt; SM
    CM --&amp;gt; OC
    P2P --&amp;gt; OC
    P2P --&amp;gt; NC
    SM --&amp;gt; NC
    OC --&amp;gt; NC
    CM --&amp;gt; CR&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;42-关键模块说明&#34;&gt;4.2 关键模块说明&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;模块&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;职责&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;技术要点&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;ConnectionManager&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;管理所有活跃 Circuit 和 Peer 连接&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;连接池、复用、超时清理&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;P2PController&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;决定是否直连、打洞、还是走中继&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ICE 候选排序、NAT 类型判断&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;SignalingClient&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;与信令服务器通信&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP 心跳、请求/响应匹配、重传&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;OnionCore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;分层加密/解密、报文打包&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;X25519、ChaCha20-Poly1305、HKDF&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;NetworkCore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;UDP Socket、STUN/ICE、NAT 类型探测&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;DatagramChannel、线程模型、电池优化&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;CircuitStore&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;本地缓存 Circuit 信息&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;SQLite / MMKV、加密存储&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;43-networkcore-线程模型&#34;&gt;4.3 NetworkCore 线程模型&lt;/h3&gt;
&lt;p&gt;UDP 网络层要避免在主线程做 IO，也避免一个线程处理所有事导致卡顿。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    subgraph 网络线程
        RT[ReceiveThread&amp;lt;br/&amp;gt;阻塞 recvFrom]
        RT --&amp;gt;|Raw Packet| DP[DecoderPipeline&amp;lt;br/&amp;gt;STUN / 洋葱 / 业务]
        DP --&amp;gt;|业务消息| MQ[MessageQueue]
        DP --&amp;gt;|信令响应| SR[SignalingResponseMap]
    end

    subgraph 发送线程
        SQ[SendQueue&amp;lt;br/&amp;gt;优先级队列]
        ST[SendThread&amp;lt;br/&amp;gt;定时 flush]
        SQ --&amp;gt; ST --&amp;gt;|sendTo| Socket
    end

    subgraph 业务线程
        BH[BusinessHandler]
        MQ --&amp;gt; BH
    end

    Socket --&amp;gt; RT&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;44-android-端-nat-类型探测&#34;&gt;4.4 Android 端 NAT 类型探测&lt;/h3&gt;
&lt;p&gt;客户端启动时，先用 STUN 服务器探测自己的 NAT 类型：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;1. 向 STUN Server A 发 Binding Request，得到映射地址 M1。
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;2. 向 STUN Server B 发 Binding Request，得到映射地址 M2。
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;3. 比较 M1 和 M2：
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   - 相同 → 可能全锥型 / 限制型
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;   - 不同 → 对称型 NAT（打洞困难）
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;4. 再测试其他 IP/Port 是否能把包送进本机，判断是限制型还是端口限制型。&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[开始探测] --&amp;gt; B[STUN A 请求]
    B --&amp;gt; C[得到 M1]
    C --&amp;gt; D[STUN B 请求]
    D --&amp;gt; E[得到 M2]
    E --&amp;gt; F{M1 == M2?}
    F --&amp;gt;|是| G[全锥 / 限制 / 端口限制]
    F --&amp;gt;|否| H[对称型 NAT]
    G --&amp;gt; I[进一步测试过滤规则]
    H --&amp;gt; J[P2P 大概率失败&amp;lt;br/&amp;gt;优先准备 Relay]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;45-android-后台保活与省电&#34;&gt;4.5 Android 后台保活与省电&lt;/h3&gt;
&lt;p&gt;P2P 需要客户端能被找到，但 Android 对后台限制越来越严。&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;方案&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;适用场景&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;注意事项&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Foreground Service&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;需要长期在线&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;必须显示通知，用户可能反感&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;WorkManager 周期性心跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;非实时场景&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;最小间隔 15 分钟，无法保证即时&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;FCM / 厂商推送&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;唤醒 App 建立连接&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;国内厂商通道需要适配&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活 socket + 心跳&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;与信令服务维持 NAT 映射&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;心跳间隔建议 15-30s， balancing 电量&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：不要试图用「双进程守护」「1 像素 Activity」等旧保活手段，Android 高版本会被杀，且应用商店可能下架。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;5-通过-udp-信令网络实现-p2p-控制&#34;&gt;5. 通过 UDP 信令网络实现 P2P 控制&lt;/h2&gt;
&lt;h3 id=&#34;51-p2p-连接的三种结果&#34;&gt;5.1 P2P 连接的三种结果&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[Peer A 想连 Peer B] --&amp;gt; B[通过信令服务获取 B 的候选地址]
    B --&amp;gt; C{尝试直连}
    C --&amp;gt;|成功| D[UDP P2P 直连]
    C --&amp;gt;|失败| E{尝试 NAT 打洞}
    E --&amp;gt;|成功| D
    E --&amp;gt;|失败| F[通过 Relay 中继]
    D --&amp;gt; G[业务通信]
    F --&amp;gt; G&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;h3 id=&#34;52-ice-like-候选地址优先级&#34;&gt;5.2 ICE-like 候选地址优先级&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;graph LR
    A[直连候选&amp;lt;br/&amp;gt;局域网 IP] --&amp;gt;|优先级最高| B[srflx 候选&amp;lt;br/&amp;gt;STUN 映射公网地址]
    B --&amp;gt;|次高| C[prflx 候选&amp;lt;br/&amp;gt;对端看到的地址]
    C --&amp;gt;|再次| D[relay 候选&amp;lt;br/&amp;gt;TURN/中继服务器]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;优先级规则：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;host 候选&lt;/strong&gt;：同一局域网直接连，延迟最低。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;server reflexive（srflx）&lt;/strong&gt;：STUN 映射后的公网地址，适合普通 NAT。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;peer reflexive（prflx）&lt;/strong&gt;：对端实际看到的地址，可能在打洞过程中发现。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;relay 候选&lt;/strong&gt;：最后兜底，走服务器转发。&lt;/li&gt;
&lt;/ol&gt;
&lt;h3 id=&#34;53-nat-打洞详细流程&#34;&gt;5.3 NAT 打洞详细流程&lt;/h3&gt;
&lt;p&gt;假设 A 和 B 都在普通家用路由器后，NAT 类型为「端口限制锥型」。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant SA as STUN/Signaling Server
    participant B as Peer B

    A-&amp;gt;&amp;gt;SA: 注册：我的 srflx 是 1.1.1.1:10000
    B-&amp;gt;&amp;gt;SA: 注册：我的 srflx 是 2.2.2.2:20000

    A-&amp;gt;&amp;gt;SA: 请求连接 B
    SA--&amp;gt;&amp;gt;A: 返回 B 的地址 2.2.2.2:20000
    SA--&amp;gt;&amp;gt;B: 转发 A 的地址 1.1.1.1:10000

    A-&amp;gt;&amp;gt;B: 发送 UDP 打洞包（可能到达或被过滤）
    B-&amp;gt;&amp;gt;A: 发送 UDP 打洞包
    Note over A,B: 双方都先发包，让各自 NAT 建立映射

    A-&amp;gt;&amp;gt;B: 收到 B 的包，回确认
    B-&amp;gt;&amp;gt;A: 收到 A 的包，回确认
    Note over A,B: 打洞成功，后续直接通信&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人记忆点&lt;/strong&gt;：NAT 打洞的本质是&lt;strong&gt;让双方路由器先记录一条「允许对方包进来」的映射&lt;/strong&gt;。所以必须双方同时主动发一个包到对方公网地址。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;h3 id=&#34;54-基于洋葱网络的-p2p-控制通道&#34;&gt;5.4 基于洋葱网络的 P2P 控制通道&lt;/h3&gt;
&lt;p&gt;除了直接 P2P，还可以把「P2P 控制信令」本身也套在洋葱网络里，增强隐私：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart LR
    A[Alice] --&amp;gt;|洋葱隧道| X[出口节点] --&amp;gt; S[信令服务器]
    B[Bob] --&amp;gt;|洋葱隧道| X&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;控制消息包括：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;请求连接、交换候选地址。&lt;/li&gt;
&lt;li&gt;协商加密密钥（用于 P2P 直连后的端到端加密）。&lt;/li&gt;
&lt;li&gt;心跳、断开通知。&lt;/li&gt;
&lt;li&gt;中继切换通知。&lt;/li&gt;
&lt;/ul&gt;
&lt;p&gt;这样即使信令服务器被监控，攻击者也只能看到「某个洋葱出口在发信令」，不知道 Alice 和 Bob 的真实身份。&lt;/p&gt;
&lt;h3 id=&#34;55-p2p-直连后的加密&#34;&gt;5.5 P2P 直连后的加密&lt;/h3&gt;
&lt;p&gt;P2P 直连本身不保证安全，还需要端到端加密：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Peer A
    participant S as Signaling Server（洋葱保护下）
    participant B as Peer B

    A-&amp;gt;&amp;gt;S: 发送 X25519 公钥 PA
    S-&amp;gt;&amp;gt;B: 转发 PA
    B-&amp;gt;&amp;gt;S: 发送 X25519 公钥 PB
    S-&amp;gt;&amp;gt;A: 转发 PB

    A-&amp;gt;&amp;gt;A: ECDH 计算共享密钥 K
    B-&amp;gt;&amp;gt;B: ECDH 计算共享密钥 K

    A-&amp;gt;&amp;gt;B: 用 K 加密业务数据，UDP P2P 直连发送
    B-&amp;gt;&amp;gt;A: 用 K 加密业务数据，UDP P2P 直连发送&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：洋葱网络的逐跳加密 ≠ P2P 端到端加密。如果数据离开出口节点后是明文，出口节点仍然能看到内容。真正保密需要在 P2P 直连后再做一次端到端加密。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;6-协议报文设计示例&#34;&gt;6. 协议报文设计示例&lt;/h2&gt;
&lt;h3 id=&#34;61-洋葱路由报文&#34;&gt;6.1 洋葱路由报文&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Version       | Command       | TTL           | Reserved        |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                        Circuit ID                             |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+              Encrypted Onion Payload (variable)               +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;Command 定义示例：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;值&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;命令&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x01&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CREATE&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;建立 Circuit&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x02&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CREATED&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;建立成功响应&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x03&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;RELAY&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;中继数据&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x04&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;DESTROY&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;销毁 Circuit&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x05&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HEARTBEAT&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活心跳&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;62-信令报文&#34;&gt;6.2 信令报文&lt;/h3&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-text&#34; data-lang=&#34;text&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0                   1                   2                   3
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt; 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1 2 3 4 5 6 7 8 9 0 1
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;| Magic (4B)    | Version (1B)  | Command (1B)  | Flags (2B)    |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         Sequence Number                       |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                         Timestamp                             |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+                    Encrypted Payload                          +
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;|                                                               |
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+-+&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;p&gt;信令 Command 示例：&lt;/p&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;值&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;命令&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;说明&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x10&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;REGISTER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;客户端注册&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x11&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;OFFER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;发起连接请求&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x12&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ANSWER&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;响应连接请求&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x13&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;CANDIDATE&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;交换候选地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x14&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;HEARTBEAT&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;保活&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;0x15&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ERROR&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;错误通知&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;blockquote&gt;
&lt;p&gt;&lt;strong&gt;新人避坑&lt;/strong&gt;：UDP 报文要加 Magic 数和版本号，防止收到错乱的广播/扫描包时解析出错。&lt;/p&gt;
&lt;/blockquote&gt;
&lt;hr&gt;
&lt;h2 id=&#34;7-关键算法与数据结构&#34;&gt;7. 关键算法与数据结构&lt;/h2&gt;
&lt;h3 id=&#34;71-circuit-选择算法&#34;&gt;7.1 Circuit 选择算法&lt;/h3&gt;
&lt;p&gt;客户端从目录服务拿到节点列表后，选择一条路径：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;div class=&#34;highlight&#34;&gt;&lt;pre tabindex=&#34;0&#34; style=&#34;color:#f8f8f2;background-color:#272822;-moz-tab-size:4;-o-tab-size:4;tab-size:4;-webkit-text-size-adjust:none;&#34;&gt;&lt;code class=&#34;language-python&#34; data-lang=&#34;python&#34;&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;def&lt;/span&gt; &lt;span style=&#34;color:#a6e22e&#34;&gt;choose_path&lt;/span&gt;(nodes, desired_hops&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;):
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 1. 过滤在线、版本兼容、flags 符合要求的节点&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    valid &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [n &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; nodes &lt;span style=&#34;color:#66d9ef&#34;&gt;if&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;is_online &lt;span style=&#34;color:#f92672&#34;&gt;and&lt;/span&gt; n&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;version &lt;span style=&#34;color:#f92672&#34;&gt;==&lt;/span&gt; PROTO_VERSION]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 2. 按负载和带宽评分，负载越低、带宽越高分越高&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scored &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; [(n, score(n)) &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; n &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; valid]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    scored&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;sort(key&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;lambda&lt;/span&gt; x: x[&lt;span style=&#34;color:#ae81ff&#34;&gt;1&lt;/span&gt;], reverse&lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt;&lt;span style=&#34;color:#66d9ef&#34;&gt;True&lt;/span&gt;)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 3. 随机选择前 K 个中的节点，避免总是选同一批&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    candidates &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; scored[:max(len(scored)&lt;span style=&#34;color:#f92672&#34;&gt;//&lt;/span&gt;&lt;span style=&#34;color:#ae81ff&#34;&gt;3&lt;/span&gt;, desired_hops &lt;span style=&#34;color:#f92672&#34;&gt;*&lt;/span&gt; &lt;span style=&#34;color:#ae81ff&#34;&gt;2&lt;/span&gt;)]
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#75715e&#34;&gt;# 4. 确保 Entry、Middle、Exit 来自不同运营商 / 网段，增加路径多样性&lt;/span&gt;
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    path &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; []
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;for&lt;/span&gt; role &lt;span style=&#34;color:#f92672&#34;&gt;in&lt;/span&gt; [&lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;entry&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;middle&amp;#39;&lt;/span&gt;, &lt;span style=&#34;color:#e6db74&#34;&gt;&amp;#39;exit&amp;#39;&lt;/span&gt;]:
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        chosen &lt;span style=&#34;color:#f92672&#34;&gt;=&lt;/span&gt; pick_diverse(path, candidates, role)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;        path&lt;span style=&#34;color:#f92672&#34;&gt;.&lt;/span&gt;append(chosen)
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    
&lt;/span&gt;&lt;/span&gt;&lt;span style=&#34;display:flex;&#34;&gt;&lt;span&gt;    &lt;span style=&#34;color:#66d9ef&#34;&gt;return&lt;/span&gt; path&lt;/span&gt;&lt;/span&gt;&lt;/code&gt;&lt;/pre&gt;&lt;/div&gt;
  &lt;/div&gt;&lt;h3 id=&#34;72-消息可靠性保证&#34;&gt;7.2 消息可靠性保证&lt;/h3&gt;
&lt;p&gt;UDP 不保证可靠，应用层需要自己实现：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TD
    A[发送消息] --&amp;gt; B[分配 Seq]
    B --&amp;gt; C[加入重传队列]
    C --&amp;gt; D[UDP 发送]
    D --&amp;gt; E{收到 ACK?}
    E --&amp;gt;|是| F[从队列移除]
    E --&amp;gt;|否| G{超时?}
    G --&amp;gt;|否| E
    G --&amp;gt;|是| H[重传 N 次]
    H --&amp;gt; I{达到最大重试?}
    I --&amp;gt;|否| C
    I --&amp;gt;|是| J[通知上层失败]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;重传策略建议：&lt;/p&gt;
&lt;ul&gt;
&lt;li&gt;初始超时：200ms（RTT 估计）。&lt;/li&gt;
&lt;li&gt;退避：指数退避，最多 5 次。&lt;/li&gt;
&lt;li&gt;ACK 去重：用滑动窗口或位图记录已确认 Seq。&lt;/li&gt;
&lt;/ul&gt;
&lt;h3 id=&#34;73-连接状态机&#34;&gt;7.3 连接状态机&lt;/h3&gt;
&lt;p&gt;P2P 连接可以建模为状态机：&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;stateDiagram-v2
    [*] --&amp;gt; Idle: 用户发起连接
    Idle --&amp;gt; Gathering: 收集 ICE 候选
    Gathering --&amp;gt; Connecting: 开始打洞/直连
    Connecting --&amp;gt; Connected: 收到对端包
    Connecting --&amp;gt; Relaying: 打洞失败，切中继
    Relaying --&amp;gt; Connected: 中继链路建立
    Connected --&amp;gt; Disconnected: 超时未收到心跳
    Disconnected --&amp;gt; [*]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;8-新人避坑技巧大全&#34;&gt;8. 新人避坑技巧大全&lt;/h2&gt;
&lt;h3 id=&#34;81-udp-层面的坑&#34;&gt;8.1 UDP 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 报文丢失&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;部分消息收不到&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;应用层 ACK + 重传；关键信令走可靠通道&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 乱序&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;消息顺序错乱&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;按 Seq 排序或丢弃过期包；业务层幂等&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;UDP 分片&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;大报文丢失率陡增&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;控制报文大小 &amp;lt; 1200 字节；必要时分片重组&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;运营商 QoS&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;高峰丢包严重&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;使用常见端口（如 443/8080）或混淆成 QUIC/DNS&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;防火墙拦截&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;某些网络 UDP 不通&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;准备 TCP 回退或 Relay 兜底&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;82-洋葱网络层面的坑&#34;&gt;8.2 洋葱网络层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Circuit 被重用&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;旧连接和新连接混用&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每个 Circuit 带唯一 ID，过期销毁&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;时序攻击&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;入口和出口流量时间关联&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;引入填充（padding）和混合延迟&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;恶意出口节点&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;出口监听或篡改明文&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;P2P 必须端到端加密，不依赖出口&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;节点列表被污染&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;连上恶意节点&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;目录服务签名 + 节点身份固定 + 信誉系统&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;密钥管理混乱&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;加密失败或性能差&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一个 Circuit 一对密钥，用 HKDF 派生&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;83-android-层面的坑&#34;&gt;8.3 Android 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;主线程网络&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;ANR / 卡顿&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;所有 UDP IO 放子线程或用协程&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;Doze 模式心跳中断&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;后台连接断开&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;使用 JobScheduler / WorkManager 补心跳&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;IPv4/IPv6 双栈&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一端 v4 一端 v6 连不上&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;同时收集 v4/v6 候选地址&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;端口复用失败&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;code&gt;BindException&lt;/code&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Socket 设置 &lt;code&gt;SO_REUSEADDR&lt;/code&gt; / &lt;code&gt;SO_REUSEPORT&lt;/code&gt;&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;电量消耗高&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;用户卸载&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;动态心跳间隔，网络空闲时延长&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;84-p2p--nat-层面的坑&#34;&gt;8.4 P2P / NAT 层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;对称型 NAT 打洞失败&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;两边都收不到包&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;提前探测 NAT 类型，准备 Relay&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;** hairpin NAT 不支持**&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;同一局域网却走公网&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;优先尝试 host 候选&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;NAT 映射超时&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;长时间没通信后连不上&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;定期发送心跳保活&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;双方不同时打洞&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;一方先发可能被防火墙拦&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;设计让 A、B 同时主动发包&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;信令与数据通道混淆&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;安全边界不清&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;信令用短期密钥，数据用长期会话密钥&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;h3 id=&#34;85-安全与隐私层面的坑&#34;&gt;8.5 安全与隐私层面的坑&lt;/h3&gt;
&lt;table&gt;
	&lt;thead&gt;
			&lt;tr&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;坑&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;现象&lt;/th&gt;
					&lt;th style=&#34;text-align: left&#34;&gt;规避方法&lt;/th&gt;
			&lt;/tr&gt;
	&lt;/thead&gt;
	&lt;tbody&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;只做了洋葱加密，没做端到端加密&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;出口节点可读明文&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;P2P 直连后再做一次 ECDH&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;时间戳不校验&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;重放攻击&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;每个报文带时间戳 + 滑动窗口&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;随机数可预测&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;Circuit ID 被猜中&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;用 SecureRandom / crypto.random&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;元数据泄露&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;包大小暴露内容类型&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;固定包长或随机填充&lt;/td&gt;
			&lt;/tr&gt;
			&lt;tr&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;&lt;strong&gt;证书/公钥不校验&lt;/strong&gt;&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;中间人攻击&lt;/td&gt;
					&lt;td style=&#34;text-align: left&#34;&gt;目录服务签名 + 证书锁定&lt;/td&gt;
			&lt;/tr&gt;
	&lt;/tbody&gt;
&lt;/table&gt;
&lt;hr&gt;
&lt;h2 id=&#34;9-一个最小可运行流程串起来&#34;&gt;9. 一个最小可运行流程串起来&lt;/h2&gt;
&lt;p&gt;为了让新人有整体感，下面把一次「Alice 通过 UDP 洋葱网络控制 P2P 连接 Bob」的完整流程串起来。&lt;/p&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;sequenceDiagram
    participant A as Android Alice
    participant D as Directory Server
    participant S as Signaling Server
    participant R as Relay Server
    participant B as Android Bob

    %% 阶段 1：准备节点
    A-&amp;gt;&amp;gt;D: HTTPS 获取节点列表
    D--&amp;gt;&amp;gt;A: 返回 Entry/Middle/Exit + 签名
    A-&amp;gt;&amp;gt;A: 校验签名，选择路径
    A-&amp;gt;&amp;gt;Entry: UDP CREATE Circuit
    Entry--&amp;gt;&amp;gt;A: CREATED
    A-&amp;gt;&amp;gt;Middle: 经 Entry 转发 CREATE
    Middle--&amp;gt;&amp;gt;A: CREATED
    A-&amp;gt;&amp;gt;Exit: 经 Entry→Middle 转发 CREATE
    Exit--&amp;gt;&amp;gt;A: CREATED

    %% 阶段 2：注册信令
    A-&amp;gt;&amp;gt;S: 经洋葱隧道 REGISTER Alice
    B-&amp;gt;&amp;gt;S: 经洋葱隧道 REGISTER Bob

    %% 阶段 3：发起 P2P
    A-&amp;gt;&amp;gt;S: OFFER（请求连接 Bob）
    S-&amp;gt;&amp;gt;B: 转发 OFFER + Alice 候选地址
    B-&amp;gt;&amp;gt;S: ANSWER + Bob 候选地址
    S-&amp;gt;&amp;gt;A: 转发 ANSWER

    %% 阶段 4：打洞
    A-&amp;gt;&amp;gt;B: UDP 打洞包
    B-&amp;gt;&amp;gt;A: UDP 打洞包
    A--&amp;gt;&amp;gt;B: 直连成功

    %% 阶段 5：业务通信
    A-&amp;gt;&amp;gt;B: 端到端加密业务数据
    B-&amp;gt;&amp;gt;A: 端到端加密业务数据

    %% 阶段 6：失败兜底
    alt 打洞失败
        A-&amp;gt;&amp;gt;S: 请求 Relay
        S--&amp;gt;&amp;gt;A: 分配 Relay 地址
        S--&amp;gt;&amp;gt;B: 通知走 Relay
        A-&amp;gt;&amp;gt;R: 经洋葱→Relay→Bob
    end&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;hr&gt;
&lt;h2 id=&#34;10-总结一张图记住全文&#34;&gt;10. 总结：一张图记住全文&lt;/h2&gt;
&lt;div class=&#34;code-block&#34;&gt;
    &lt;button class=&#34;code-copy&#34; type=&#34;button&#34; hidden aria-label=&#34;Copy code to clipboard&#34;&gt;
      &lt;span class=&#34;code-copy-label&#34; aria-hidden=&#34;true&#34;&gt;Copy&lt;/span&gt;
    &lt;/button&gt;
    &lt;pre tabindex=&#34;0&#34;&gt;&lt;code class=&#34;language-mermaid&#34; data-lang=&#34;mermaid&#34;&gt;flowchart TB
    A[UDP 洋葱网络 + P2P] --&amp;gt; B[目录服务&amp;lt;br/&amp;gt;给节点]
    A --&amp;gt; C[信令服务&amp;lt;br/&amp;gt;撮合 Peer]
    A --&amp;gt; D[中继服务&amp;lt;br/&amp;gt;兜底转发]
    A --&amp;gt; E[Android 客户端&amp;lt;br/&amp;gt;洋葱 + NAT + P2P]
    E --&amp;gt; F[OnionCore&amp;lt;br/&amp;gt;逐跳加密]
    E --&amp;gt; G[NetworkCore&amp;lt;br/&amp;gt;UDP / STUN / ICE]
    E --&amp;gt; H[P2PController&amp;lt;br/&amp;gt;直连 / 打洞 / 中继]
    F --&amp;gt; I[隐私保护]
    G --&amp;gt; J[NAT 穿透]
    H --&amp;gt; K[去中心化通信]
    A --&amp;gt; L[避坑：可靠传输 / 端到端加密 / 后台保活]&lt;/code&gt;&lt;/pre&gt;
  &lt;/div&gt;&lt;p&gt;核心要点回顾：&lt;/p&gt;
&lt;ol&gt;
&lt;li&gt;&lt;strong&gt;洋葱网络用 UDP 是为了轻量、NAT 友好、适合信令&lt;/strong&gt;；但可靠性和顺序要自己补。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;服务器分三类&lt;/strong&gt;：目录服务（给节点）、信令服务（撮合 P2P）、中继服务（兜底转发）。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;Android 客户端分层&lt;/strong&gt;：业务层、连接管理层、核心引擎层、网络层，不要混在一处。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;P2P 控制靠 UDP 信令交换地址&lt;/strong&gt;，再用 ICE 候选优先级尝试直连、打洞、中继。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;隐私不等于安全&lt;/strong&gt;：洋葱网络保护路径隐私，P2P 业务数据还要端到端加密。&lt;/li&gt;
&lt;li&gt;&lt;strong&gt;新人最容易踩的坑&lt;/strong&gt;：UDP 当 TCP 用、忽略 NAT 类型、不做端到端加密、Android 主线程 IO。&lt;/li&gt;
&lt;/ol&gt;
&lt;hr&gt;
&lt;h2 id=&#34;参考与延伸阅读&#34;&gt;参考与延伸阅读&lt;/h2&gt;
&lt;ul&gt;
&lt;li&gt;Tor Project: &lt;a href=&#34;https://www.torproject.org/&#34;&gt;https://www.torproject.org/&lt;/a&gt;&lt;/li&gt;
&lt;li&gt;ICE / STUN / TURN RFCs: RFC 5245, RFC 5389, RFC 5766&lt;/li&gt;
&lt;li&gt;WebRTC NAT Traversal 实践&lt;/li&gt;
&lt;li&gt;Android &lt;code&gt;DatagramSocket&lt;/code&gt;、&lt;code&gt;WorkManager&lt;/code&gt;、&lt;code&gt;Foreground Service&lt;/code&gt; 官方文档&lt;/li&gt;
&lt;/ul&gt;
&lt;hr&gt;
&lt;blockquote&gt;
&lt;p&gt;如果你正在动手实现，建议先做一个「最小闭环」：一个目录服务 + 一个中继节点 + 两个 Android 客户端，先跑通「Alice 经洋葱网络发一条消息给 Bob」，再逐步加入 P2P 直连和 NAT 打洞。先跑通，再优化。&lt;/p&gt;
&lt;/blockquote&gt;
</description>
    </item>
    
  </channel>
</rss>
