P2P 信令与中继服务器技术全面调研

自建 P2P 信令与中继服务器,是跨网互联、远程访问、Mesh VPN 等场景的核心基建。本文系统梳理了从 NAT 穿透协议到产品实践的完整技术图谱,覆盖三个层面:协议标准(STUN/TURN/ICE/BEHAVE)、主流产品(Tailscale、Nebula、NetBird、ZeroTier、Headscale、OpenZiti 等)、以及框架与算法(libp2p、WebRTC、Kademlia DHT)。

所有技术描述均基于 RFC 原文、学术论文、官方文档的一手来源核实,关键统计数据标注出处。


核心概念速查

在深入技术细节前,先用一张表快速理解本文涉及的核心概念。每个概念在后续章节会有详细展开。

概念一句话解释
NAT网络地址转换,将内网私有 IP 映射为公网 IP,让多台设备共享一个公网出口
STUN让设备发现自己公网地址的协议(“我在外界看来是谁?")
TURN当直连失败时,由服务器中继转发流量的协议(“帮我传话”)
ICE编排 STUN + TURN 的完整方案,自动找到最优连接路径(“总指挥”)
DERPTailscale 自研的加密中继,走 HTTPS 443 端口,极难被封
WireGuard现代 VPN 协议,约 4000 行代码,使用 Curve25519 + ChaCha20-Poly1305
Noise 协议密码学协议框架,用于构建安全的传输通道,Nebula 基于此实现
信令通道独立于业务流量的控制通道,用于交换地址、协调打洞(“边带通道”)
打洞(Hole Punching)双方同时向对方发包,在各自 NAT 上打开映射,建立直连
控制平面 / 数据平面控制平面管"谁能连谁”,数据平面管"实际数据怎么走",两者分离
DHT分布式哈希表,无中心服务器的 P2P 节点发现与数据定位机制
Mesh VPN网状 VPN,节点间直接互联而非星形汇聚,无单点故障
LighthouseNebula 的发现节点,类似 DNS——回答"某节点在哪?"
PKI / CA公钥基础设施 / 证书权威,用于身份认证与加密
CGNAT运营商级 NAT,多用户共享少量公网 IP,是打洞的常见障碍

NAT 穿透:直连的第一道门槛

两台分别位于不同 NAT 后的设备要建立 P2P 直连,最大的障碍就是 NAT。NAT(Network Address Translation,网络地址转换)是一种将内网私有 IP 地址映射为公网 IP 地址的技术——家庭路由器和运营商网关都在做这件事。它让多台设备共享一个公网 IP 出口,但也使得外部无法主动向内网发起连接,这是所有穿透技术要解决的核心矛盾。

理解 NAT 的行为模型,是所有穿透技术的理论基础。

NAT 行为的二维描述

RFC 4787(BCP 127)用两个独立维度描述 NAT 行为,取代了早期 Cone/Symmetric(锥形/对称型)的粗粒度分类:

  • 映射行为(Mapping Behavior)——同一内部 (IP, port) 到不同目的地址通信时,NAT 是否复用同一外部映射?换句话说,你从家里访问 A 网站和 B 网站时,路由器给你分配的外部端口是否相同?
    • Endpoint-Independent Mapping(端点无关映射):总是复用同一映射(≈ Full Cone 的映射维度)。无论你访问谁,外部端口都一样——这是最好的情况,打洞最容易
    • Address-Dependent Mapping(地址相关映射):换目的 IP 则换映射。访问不同 IP 时外部端口不同
    • Address and Port-Dependent Mapping(地址和端口相关映射):换目的 IP 或端口则换映射(≈ Symmetric)。这是最差的情况,几乎无法打洞
  • 过滤行为(Filtering Behavior)——外部主机向 NAT 映射地址发包时,NAT 何时放行?即,外部来的包要满足什么条件才被转发到内网?
    • Endpoint-Independent Filtering(端点无关过滤):任意外部主机可发——最宽松
    • Address-Dependent Filtering(地址相关过滤):仅当内部此前向该 IP 发过包——只回信给联系过的人
    • Address and Port-Dependent Filtering(地址和端口相关过滤):仅当内部此前向该 (IP, port) 发过包——最严格

RFC 4787 REQ-1 明确要求:NAT 必须具备 Endpoint-Independent Mapping,否则几乎所有打洞技术都将失效,被迫依赖中继。

四种经典 NAT 类型

虽然 RFC 3489 的分类已被 RFC 5389 废止(因"造成了大量混淆"),但其四类模型仍是理解打洞失败原因的最佳入门工具:

类型映射特征外部可达性打洞可行性
Full Cone(完全锥形)同一内部 (IP,port) → 同一外部 (IP,port)任意外部主机可达✅ 最容易
Restricted Cone(受限锥形)同上仅限内部曾发过包的 IP✅ 需协调
Port Restricted Cone(端口受限锥形)同上仅限内部曾发过包的 (IP,port)✅ 需精确协调
Symmetric(对称型)不同目的 → 不同映射仅接收过包的源❌ 几乎不可能

对称型 NAT(Symmetric NAT) 是 P2P 直连的最大障碍——它为每个目的地分配不同的外部端口,使得通过会合服务器交换的公网地址在向对端发包时不再有效。

会合服务器(Rendezvous Server) 是一台位于公网的中介服务器,两端各自向它注册自己的地址信息,它负责将一方的地址告知另一方,为打洞做准备。

UDP 打洞原理

UDP 打洞(Hole Punching)是 NAT 穿越中最简单、最健壮的技术。Bryan Ford 等人在 USENIX ‘05 的经典论文中系统化文档化并实测了这一技术:

mermaid
sequenceDiagram
    participant A as 节点 A
    participant S as 会合服务器
    participant B as 节点 B
    A->>S: 注册(报告自身地址)
    B->>S: 注册(报告自身地址)
    S-->>A: 返回 B 的公网映射
    S-->>B: 返回 A 的公网映射
    Note over A,B: 双方同时向对方公网地址发包
    A->>B: UDP 包(在本端 NAT 开洞)
    B->>A: UDP 包(在本端 NAT 开洞)
    Note over A,B: NAT 映射建立,双向通信成功

核心流程:两端各自向公网会合服务器注册 → 服务器告知对方的公网反射地址 → 双方同时从本地 socket 向对方发包 → 出站包在各自 NAT 上建立映射(“洞”)→ 后续对端发来的包被 NAT 视为"响应"而放行。

实测数据(Ford et al., USENIX ATC 2005):约 82% 的 NAT 支持 UDP 打洞,约 64% 支持 TCP 打洞。论文还首次可靠展示了基于 TCP simultaneous-open 的 P2P TCP 流建立。

TCP 打洞:可行但脆弱

TCP 打洞利用 RFC 793 定义的 simultaneous-open(同时打开) 模式——正常 TCP 连接是一端发 SYN(主动打开)、另一端回 SYN-ACK(被动打开);而 simultaneous-open 是两端几乎同时互发 SYN,SYN 在网络中交错,各自以 SYN-ACK 响应,连接建立。这要求 NAT 支持端点无关映射且正确处理 simultaneous-open(RFC 5382 REQ-2)。

比 UDP 更脆弱的原因:TCP 状态机复杂(需要正确处理 SYN/SYN-ACK/ACK 三次握手中的各种异常组合)、部分 NAT 会丢弃入站 SYN 或错误转换出站 SYN-ACK。业界推荐以 UDP 为基础协议(如 QUIC over UDP)。

QUIC 是 Google 开发、HTTP/3 使用的传输协议,运行在 UDP 之上,内置加密(TLS 1.3),解决了 TCP 队头阻塞问题。用 UDP 承载既能获得类似 TCP 的可靠传输,又绕过了 TCP 打洞的困难。


标准化穿透协议栈

NAT 穿透技术栈自下而上分为多层,每层解决不同问题:

mermaid
flowchart TD
    A["BEHAVE 行为规范<br/>定义 NAT 该如何表现"] --> B["打洞技术<br/>UDP/TCP Hole Punching"]
    B --> C["STUN<br/>发现反射地址"]
    C --> D["TURN<br/>中继回退"]
    C --> E["ICE<br/>编排 STUN + TURN"]
    E --> F["DERP<br/>加密中继补充"]
    style A fill:#fff3e0,stroke:#FF9800
    style B fill:#e3f2fd,stroke:#2196F3
    style C fill:#e3f2fd,stroke:#2196F3
    style D fill:#e3f2fd,stroke:#2196F3
    style E fill:#f3e5f5,stroke:#9C27B0
    style F fill:#e8f5e9,stroke:#4CAF50

最底层是 BEHAVE 工作组制定的 NAT 行为规范——它不定义穿越技术,而是定义"一个合格的 NAT 应该怎么表现"。往上是打洞技术(实际穿越手段),再上是 STUN(地址发现工具)和 TURN(中继回退工具),ICE 将这些工具编排为完整方案,最顶层是 Tailscale 的 DERP 补充中继设计。

STUN:发现你的公网地址

RFC 5389(2008),作者 J. Rosenberg 等。STUN(Session Traversal Utilities for NAT)原名 “Simple Traversal of UDP through NAT”,因定位从"完整方案"改为"工具"而更名——它不再是独立解决 NAT 穿越的完整系统,而是一个供其他协议调用的工具。

核心方法是 Binding(绑定):客户端向 STUN 服务器发送 Binding Request 请求,报文经过 NAT 时源地址被 NAT 改写;服务器收到后,将观察到的公网源地址(即 reflexive transport address——反射传输地址,就是"你在 NAT 外面看起来是什么地址")放入 XOR-MAPPED-ADDRESS 属性返回给客户端。

简单说,STUN 就像你问朋友"从我这边打电话给你,你的来电显示是什么号码?"——朋友看到的号码就是你的公网映射地址。

STUN 是一个工具而非完整方案,通过 “STUN usages”(用法)被嵌入 ICE、SIP Outbound 等完整方案中。

重要警示:RFC 5389 明确指出 classic STUN(RFC 3489)的 NAT 类型分类算法"有缺陷",许多真实 NAT 无法干净归类。

TURN:中继回退

RFC 5766(2010),被 RFC 8656 更新。TURN(Traversal Using Relays around NAT)是 STUN 的扩展(大部分报文是 STUN 格式)。当两端位于"行为不良"的 NAT 之后,打洞会失败,此时需要 TURN 中继服务器转发数据包——它就像一个邮局,帮你转交信件。

客户端通过 TURN 命令在服务器上创建 Allocation(分配)——就是在服务器上"租"一个中继端口——获得一个 relayed transport address(中继传输地址)。对端向该地址发包,服务器转发给客户端。

关键机制:

  • Permissions(权限):控制哪些对端可以经此中继通信,防止未授权访问
  • Channels(通道):一种更高效的数据发送方式,用 4 字节通道号代替每次发送时的完整地址信息,减少开销
  • Allocation 刷新:中继分配有生命周期,需定期刷新维持,防止资源泄漏
  • 独有特性:单一中继地址可同时与多个对端通信(为 SIP forking——同一呼叫拨给多个目的地——设计)

设计哲学:TURN 服务器带宽成本高(所有流量都经过它),应仅在 ICE 找不到直连路径时作为最后手段使用。

ICE:编排一切

RFC 8445(2018),废止了 RFC 5245。ICE(Interactive Connectivity Establishment)是基于 offer/answer 方法论(一端发出连接提议 offer,另一端回复应答 answer)的 NAT 穿越完整方案,编排 STUN 与 TURN。

mermaid
flowchart TD
    A["收集候选地址<br/>Host · SRFLX · Relay"] --> B["优先级排序<br/>并通过信令通道交换"]
    B --> C["STUN 连通性检查<br/>逐一测试候选对"]
    C --> D{"找到有效路径?"}
    D -->|"是"| E["提名并切换直连 ✓"]
    D -->|"否"| F["TURN/DERP<br/>中继回退"]
    style A fill:#e3f2fd,stroke:#2196F3
    style E fill:#e8f5e9,stroke:#4CAF50
    style F fill:#fce4ec,stroke:#f44336

ICE 的核心阶段:

  • 收集候选地址(Gathering Candidates):每端收集多类候选——
    • Host candidates(主机候选):本机所有网络接口地址(如 192.168.1.100)
    • Server-Reflexive candidates(服务器反射候选,简称 SRFLX):通过 STUN 获取的 NAT 公网映射地址
    • Relayed candidates(中继候选):通过 TURN 获取的中继地址
    • 运行中还可能发现 Peer-Reflexive candidates(对端反射候选):连通性检查中对端观察到的地址
  • 优先级排序与交换:按公式计算优先级(Host > SRFLX > Relay),通过信令通道(如 SDP——Session Description Protocol,会话描述协议,用于描述连接参数的明文文本格式)交换候选信息
  • 连通性检查(Connectivity Checks):用 STUN Binding 请求成对测试候选对——“这条路径通不通?“采用 triggered-check(触发检查)、角色冲突解决(ICE-Controlling / ICE-Controlled,决定谁来主导提名的角色)、USE-CANDIDATE 提名等机制
  • 提名与结论(Nominating & Concluding):选定有效对,结束 ICE 处理,释放多余候选

ICE 的价值在于兼容各种网络拓扑——它不假设单一方案有效,而是收集所有可能的路径并逐一测试,最终选择最优的那条。

DERP:Tailscale 的加密中继创新

DERP(Designated Encrypted Relay for Packets)是 Tailscale 自研的中继协议,非 IETF RFC 标准,权威来源为 Tailscale 官方文档与源码。

设计要点:

  • 零知识转发:DERP 永不终止或解密 WireGuard 加密——它只是盲目转发已加密流量。Tailscale 私钥从不离开本地设备,DERP 服务器无法解密任何流量,即使 DERP 服务器被攻破也不会泄露通信内容
  • 走 HTTPS(TCP 443):443 是 HTTPS 标准端口,几乎所有网络都放行。要封禁 DERP 就得封 443,但这会同时阻断所有正常 Web 访问——几乎不可能在不引起注意的情况下被封禁
  • 公钥寻址:使用 curve25519(一种椭圆曲线加密算法,提供 128 位安全强度)公钥作为地址路由数据包——不依赖 IP 地址,公钥就是你的"门牌号”
  • 双栈支持:IPv4/IPv6 全支持,可桥接纯 v4 网络与纯 v6 网络
  • 多区域选路:协调服务器下发 DERP Map(DERP 服务器列表),客户端按网络延迟选择最近的 home DERP(主 DERP 节点)

DERP 兼作"边带通道(side channel)“用于交换 ip:port 信息以协调打洞时机——绝大多数连接先用 DERP 交换信息,然后升级为直连。

mermaid
flowchart TD
    CO["协调服务器<br/>下发 DERP Map"] --> DA["DERP 区域 A<br/>TCP:443"]
    CO --> DB["DERP 区域 B<br/>TCP:443"]
    NA["节点 A<br/>硬 NAT 后"] -->|"HTTPS 加密转发"| DA
    NB["节点 B<br/>硬 NAT 后"] -->|"HTTPS 加密转发"| DB
    style CO fill:#f3e5f5,stroke:#9C27B0
    style DA fill:#fff3e0,stroke:#FF9800
    style DB fill:#fff3e0,stroke:#FF9800
    style NA fill:#e3f2fd,stroke:#2196F3
    style NB fill:#e3f2fd,stroke:#2196F3

DERP Map 由协调服务器下发,客户端按延迟选择最近的 DERP 节点。当直连 UDP 路径无法建立时(硬 NAT、防火墙阻断 UDP),DERP 在 TCP 443 上转发已加密的 WireGuard 数据包。单节点故障切换同区域其他节点;整区域故障切换最近区域。


控制平面与数据平面:行业架构共识

现代 Mesh VPN 普遍采用控制平面与数据平面分离的架构。这是最重要的架构原则。

  • 控制平面(Control Plane):负责"谁能连谁”——设备注册、身份认证、密钥分发、策略下发(ACL——访问控制列表,定义哪些节点可以互通)、NAT 穿越协调。不接触任何业务数据
  • 数据平面(Data Plane):负责"实际数据怎么走”——节点间的端到端加密隧道,数据加密/解密/路由全部在节点本地完成
mermaid
flowchart TD
    CS["协调服务器<br/>发现 · 密钥 · ACL · NAT 协调"]
    CS -.->|"仅元数据"| A["节点 A"]
    CS -.->|"仅元数据"| B["节点 B"]
    A ===|"P2P 加密直连<br/>WireGuard/Noise"| B
    style CS fill:#fff3e0,stroke:#FF9800
    style A fill:#e3f2fd,stroke:#2196F3
    style B fill:#e3f2fd,stroke:#2196F3

虚线表示控制平面(元数据交换:谁在线、公钥是什么、地址是什么),实线表示数据平面(端到端加密隧道:实际的业务流量)。协调服务器看得见元数据但看不到业务流量——即使协调服务器被入侵,攻击者也无法获取通信内容。

代表产品的架构实践

产品控制平面数据平面中继回退
Tailscale官方协调服务器(闭源)WireGuard(用户态)DERP(HTTPS/TCP 443)
NebulaLighthouse(自托管)Noise 协议(自带实现)Lighthouse 可配为 relay
ZeroTierPlanet Root + ControllerSalsa20/Poly1305(VL1)Root 转发 + network relays
NetBird管理服务 + signal + relayWireGuard自建 relay
Headscale自建控制面(Tailscale 开源替代)复用 Tailscale 客户端内嵌 DERP + Peer relays
OpenZitiController + Router内置 overlay 加密Edge Router 转发

WireGuard 是现代 VPN 协议,目标是简单(约 4000 行代码,远小于 IPSec 的数十万行)、快速、安全。使用 Curve25519(密钥交换)、BLAKE2(哈希)、ChaCha20-Poly1305(加密)等现代加密原语。Tailscale/NetBird/Netmaker 均以 WireGuard 为数据平面基础。

Noise 协议框架 是构建安全传输协议的密码学框架(WireGuard 也基于它)。Nebula 自带了基于 Noise 的传输实现,而非直接用 WireGuard。

Tailscale:DERP 双层设计的标杆

Tailscale 的 DERP 承担双重角色:(1) 连接协商中介——绝大多数连接仅用 DERP 交换信息后即升级直连;(2) 直连失败时的 fallback 流量中继。连接技术上总是先经 DERP 建立,然后并行尝试直连打洞。成功则无缝切换,典型条件下直连成功率超过 90%。

难点是对称 NAT(hard NAT)随机化源端口映射,使 P2P 几乎不可能;多层 NAT、企业防火墙阻断 UDP、运营商级 CGNAT 会触发 DERP fallback。Tailscale 还赞助了 FreeBSD PF 防火墙的 Endpoint-Independent Mapping (EIM) 补丁,使 pfSense/OPNsense 等设备从对称 NAT 变为锥形 NAT,改善打洞成功率。

Nebula:去中心化 PKI + Lighthouse

Nebula(Slack 开源)基于 Noise 协议框架的互认证 P2P 软件定义网络。

  • 去中心化 PKI/CA:每个网络拥有自己的证书权威(CA),证书断言节点 IP、名称与组成员关系。PKI(Public Key Infrastructure,公钥基础设施) 是一套用数字证书管理公钥的体系,CA(Certificate Authority,证书权威) 是签发证书的可信第三方
  • Lighthouse(灯塔节点):IP 不变的发现节点,类似 DNS 服务器——回答"主机 X 在哪?“查询,返回其最后已知的外部 endpoint。Lighthouse 默认不转发流量,只做发现协调。当打洞失败时,可将 Lighthouse 配置为 relay

NetBird:全栈开源的 Tailscale 替代

NetBird 是全栈开源 + 完整自建协调基础设施的方案,直击 Tailscale 闭源控制平面痛点。管理服务、signal server(信令服务器)、relay routing(中继路由)全部可自建,适合 GDPR/HIPAA/SOC 2(欧盟数据保护/美国医疗信息/安全审计标准)等合规敏感场景。客户端(含 iOS/Android)也全部开源。

自建友好度排序

mermaid
flowchart TD
    A["完全自建友好<br/>NetBird · Nebula<br/>OpenZiti · Headscale"] --> B["部分自建<br/>ZeroTier<br/>Netmaker"]
    B --> C["控制面闭源<br/>Tailscale<br/>(需 Headscale 替代)"]
    style A fill:#e8f5e9,stroke:#4CAF50
    style B fill:#fff3e0,stroke:#FF9800
    style C fill:#fce4ec,stroke:#f44336

NetBird 因全栈开源、自带 signal server + relay,是最直接的参考实现。Headscale 实现了 Tailscale 控制平面的开源自建替代,数据平面仍使用官方开源客户端。


去中心化穿越的新范式

libp2p 的 NAT 穿透体系借鉴了 ICE 协议,但去除了对中心化 STUN/TURN 服务器的依赖,采用分布式协调。libp2p 是 Protocol Labs 开发的模块化 P2P 网络栈,被 IPFS、Ethereum 等主要 P2P 网络采用,提供节点发现、连接建立、流多路复用和安全通信等基础工具。

libp2p 三大核心模块

模块功能对应 ICE 角色
AutoNAT判断节点是否在 NAT 后。请求其他 peer 回拨自己的地址——成功=public(公网可达),失败=private(在 NAT 后)类似 STUN
Identify连接建立后双方交换信息,学习对端观察到的自身外部公网 IP:port。利用已有连接即可完成,无需独立的 STUN 服务器基础设施类似去中心化 STUN
Circuit Relay v2为 private peer 提供轻量中继。需先获得 reservation(预约),对连接数/时长/数据量严格限制,使多数 public 节点都可作中继而资源开销极小类似 TURN(仅转发信令,非全量流量)

DCUtR:从中继升级到直连

DCUtR(Direct Connection Upgrade through Relay) 用于将已建立的中继连接升级为直连。它的核心思想是利用已有中继通道协调打洞时机,然后双方同时发起直连拨号:

mermaid
sequenceDiagram
    participant I as 发起方
    participant R as 中继节点
    participant L as 监听方
    I->>R: 建立中继连接
    R->>L: 转发连接请求
    Note over I,L: 交换 Connect 消息(含各自非中继地址)
    Note over I,L: Initiator 测量 RTT,发送 Sync
    Note over I,L: 等待半个 RTT 后双方同时拨号
    I->>L: 直接拨号(打洞)
    L->>I: 直接拨号(打洞)
    Note over I,L: 直连建立,中继连接释放

关键设计:协议极轻量,正常情况仅需 2 次网络往返,每方向交换 <500 字节。双方同时拨号使得 5-tuple(五元组:源IP、源端口、目的IP、目的端口、协议) 在各自路由器状态表匹配 → 打洞成功。

大规模实测数据

IMC 2026 论文(Trautwein et al.)对 DCUtR 在 IPFS 生产网络进行了大规模测量,基于 440 万次穿越尝试、85,000+ 网络、167 国家:

给定 relay reservation 和公网地址发现成功后,打洞阶段条件成功率 70% ± 7.1%

TCP 与 QUIC 成功率无统计差异(均约 70%),挑战了"UDP 穿越必然更优"的传统认知——DCUtR 的 RTT 同步机制使两者等效。

97.6% 的成功连接在首次尝试建立。

WebRTC:浏览器中的 P2P

WebRTC(Web Real-Time Communication)是 W3C 标准化的浏览器 P2P 通信框架,支持音视频通话和数据传输。WebRTC 采用 ICE 框架系统性寻找最优通信路径。

一个特别的设计:信令协议本身不由 WebRTC 规定——它只定义如何传输音视频和数据,至于"怎么交换连接信息"留给应用层自行实现(常用 WebSocket——一种在单个 TCP 连接上进行全双工通信的协议)。信令交换使用 SDP(Session Description Protocol) 明文协议,包含可达 IP:port 候选、音视频轨道数、编解码器列表、加密参数等。

WebRTC 的 DataChannel(RFC 8831)支持 P2P 任意数据传输,底层基于 SCTP over DTLS——SCTP(Stream Control Transmission Protocol)是支持多路复用的传输协议,DTLS(Datagram TLS)是 TLS 的 UDP 版本,提供加密。这一组合让浏览器间可以直接传输任意数据。


经典 DHT 算法速览

DHT(Distributed Hash Table,分布式哈希表) 是 P2P 网络的节点发现与数据定位基础——它像一个去中心化的字典,没有中心服务器,每个节点存储一部分数据,通过协同查找定位任意 key。三大经典算法奠定了现代 P2P 系统的路由架构。

Kademlia(2002)

Petar Maymounkov & David Mazières 设计的基于 XOR 度量的 DHT。节点 ID 与 key 共享同一 160 位标识空间,距离定义为两者的 XOR(异或运算:相同为 0,不同为 1)。XOR 距离满足度量空间的数学性质(自反性、对称性、三角不等式),使得路由可以高效收敛。

核心创新:

  • 节点可从收到的查询中学习路由信息——与 Chord 不同,Kademlia 的每次查询都在同时更新路由表
  • 路由表非刚性——查询可发往区间内任意节点,允许基于延迟选路甚至并行发送异步查询
  • k-bucket(k 桶) 结构:每个节点维护一组"桶”,每个桶覆盖一段距离区间,存放到该区间最近的 k 个节点信息。这种结构天然适应节点动态加入/离开

被 eMule(Kad Network)、Ethereum 节点发现协议、BitTorrent Mainline DHT 广泛采用。

Chord(2001, SIGCOMM)

Ion Stoica 等设计的基于**一致性哈希(Consistent Hashing)**的 DHT。一致性哈希是一种特殊的哈希分配策略,使得节点加入/离开时只有最小范围的数据需要重新分配(而非全局重哈希),是分布式系统负载均衡的经典技术。

核心是 finger table(指取表):每个节点维护 O(log N) 个路由表项,指向标识环上特定距离的节点。通信开销和状态随节点数对数增长,即 100 万个节点只需约 20 个表项——极其高效。

Pastry(2001, Middleware)

Rowstron & Druschel 设计的混合路由方案,结合数值路由前缀匹配。节点 ID 按字符串前缀组织:路由每跳使与目的地的共同前缀增加一位,就像邮政编码逐级定位。节点维护路由表 + 叶子集(leaf set,数字上最近的节点) + 邻居集(neighborhood set,网络延迟上最近的节点),跳数 O(log N)。


中继与会话韧性的工程智慧

Mosh:无状态漫游的优雅范式

Mosh(Mobile Shell)是 MIT 的 Keith Winstein & Hari Balakrishnan 开发的远程终端工具,解决 SSH 在移动网络下频繁断线的问题。Mosh 基于 State Synchronization Protocol(SSP,状态同步协议) 实现——一个运行在 UDP 上的新协议,在 client 和 server 间安全同步终端状态。

其核心创新是无状态漫游(stateless roaming)

每当 server 收到 client 发来的、序列号高于此前所有收到包的认证包时,该包的 IP 源地址即成为 server 出包的新目标地址

这意味着 client 的 IP 地址变化时(如从 WiFi 切换到 4G,或手机信号切换基站),无需任何重连操作——server 自动跟踪到新地址。client 甚至无需感知自己的公网 IP 已变化。这一思路为 P2P 中继场景下的"会话迁移 / IP 漂移后无缝续接"提供了直接借鉴。

Mosh 还有本地回显(Local Echo) 机制:client 预测用户击键效果并投机显示,无需等待 server 回显 → 弱网下打字响应迅速。

Tor / I2P:分层中继的匿名性参考

Tor(The Onion Router,洋葱路由) 的核心机制是 Circuit(电路) 匿名。Tor 有四类 relay 节点:

  • Guard/Entry Relay(入口):知道 client IP 但不知目的地
  • Middle Relay(中间):只知前后跳 IP
  • Exit Relay(出口):知目的地但不知 client
  • Bridge Relay(网桥):用于绕过 Tor 网络封锁

结构特性为"仅前驱/后继知识":每个 relay 只知前一跳和后一跳 IP。只要 guard 和 exit 不共谋,匿名性成立。

I2P(Invisible Internet Project) 使用大蒜路由(Garlic Routing)单向隧道(unidirectional tunnels)——分 inbound(入站)和 outbound(出站)隧道,每条隧道仅单向传输。一次完整消息交换需要 4 条隧道。

虽然匿名性不是 P2P 信令的核心目标,但 Tor/I2P 的"零知识中继"理念与 DERP 的"不解密、仅转发"设计异曲同工。

frp / ngrok:反向代理的工程实践

frp(Fast Reverse Proxy) 是 Go 语言编写的开源内网穿透工具。frp 的"控制通道 + 数据隧道复用“分离设计值得借鉴:frpc(客户端,部署在内网)主动出站连接 frps(服务端,部署在公网 VPS)的 bind_port 建立持久 TCP 长连接(控制通道);frpc 告知 frps 自己能代理的服务;外部访问 frps 公网端口时,frps 通过控制通道通知 frpc,数据经隧道复用转发。

这与 P2P 的"信令通道 + 数据通道分离"架构理念一致——控制信令走一条路,业务数据走另一条路。

ngrok 提供类似功能的 SaaS 服务,一键启动,集成认证与监控。bore 是轻量级的 Rust/Tokio 实现替代品。三者的共同模型是集中式中继(与 P2P 直连目标不同),但其反向连接突破入站限制的工程实践可直接借鉴。


自建信令与中继:八条工程启示

基于上述全面调研,总结自建 P2P 信令与中继服务器的八条核心结论:

信令通道是先决条件

所有打洞技术都依赖一个独立的"边带/信令通道"来交换候选地址并协调打洞时机。Tailscale 用协调服务器 + DERP 充当此通道,WebRTC 要求自备 signaling channel。项目设计应首先明确信令服务器的职责边界——它只负责协调,不接触业务流量。

分层回退是标准范式

业界共识是 STUN 反射地址发现 → UDP 打洞 → TURN/DERP 中继的递进回退。ICE 把这套流程标准化;DERP 是 Tailscale 在 TURN 之外的 HTTPS/TCP 中继补充(更难被封)。不要指望一种方案解决所有场景——必须有 fallback。

对称型 NAT 是主要障碍

Endpoint-Dependent Mapping(≈ Symmetric)的 NAT 无法常规打洞,必须依赖中继或端口预测。CGNAT(运营商级 NAT) 让大量用户共享少量公网 IP,进一步加剧了这个问题。BEHAVE(RFC 4787 REQ-1)正是为消除该障碍而要求 NAT 具备端点无关映射。

推荐以 UDP 为基础协议

TCP 打洞可行但脆弱(需 simultaneous-open,成功率约 64%),推荐以 UDP 为基础协议。QUIC over UDP 是现代选择——获得可靠传输的同时避开 TCP 打洞的困难。

中继服务器应"零知识”

DERP 的设计启示:中继服务器应不解密、仅转发加密包;走 443 端口以提升可达性;支持双栈与多区域选路;兼作发现/协调通道。这样即使中继服务器被攻破或被要求交出数据,也无法泄露通信内容。

控制平面与数据平面解耦

Tailscale 的 coordination server、Nebula 的 Lighthouse、ZeroTier 的 root+controller、OpenZiti 的 Controller 均只做协调/发现/密钥/策略,不承载业务流量。这是自建系统应遵循的架构原则——协调服务器可以宕机,只要节点间已建立直连,通信不受影响。

会话韧性设计

Mosh 的无状态漫游(序列号驱动 IP 迁移)为中继场景的断线重连提供优雅范式,值得在自建中继中实现。当用户 IP 漂移(移动网络切换、WiFi 断重连)时,中继服务器自动跟踪新地址,无需重建会话。

自建技术选型优先级

NetBird / Nebula / OpenZiti / Headscale > ZeroTier > Tailscale(控制面闭源)。NetBird 因全栈开源、自带 signal server + relay,是最直接的参考实现。


参考来源

RFC 标准

RFC标题说明
RFC 5389STUNSession Traversal Utilities for NAT
RFC 5766TURNTraversal Using Relays around NAT
RFC 8656TURN(更新版)IPv6/IPv4 支持
RFC 8445ICEInteractive Connectivity Establishment
RFC 3489Classic STUNNAT 类型分类(已废止)
RFC 5128P2P across NAT所有已知穿越方法文档化
RFC 4787NAT UDP 行为要求BCP 127
RFC 5382NAT TCP 行为要求BCP 142
RFC 8831WebRTC Data ChannelsP2P 数据通道

学术论文

#论文作者年份/会议
1Peer-to-Peer Communication Across NATsFord, Srisuresh, KegelUSENIX ATC 2005
2Kademlia: A P2P Information System Based on XORMaymounkov, Mazières2002
3Chord: A Scalable P2P Lookup ServiceStoica et al.SIGCOMM 2001
4Pastry: Scalable Decentralized Object LocationRowstron, DruschelMiddleware 2001
5Large-Scale Measurement of NAT Traversal (DCUtR)Trautwein et al.IMC 2026
6WireGuard Formal ProofINRIA2019
7Mosh: Interactive Remote Shell for MobileWinstein, BalakrishnanMIT 2012

产品文档与源码

产品GitHub协议官方文档
Tailscaletailscale/tailscaleBSD-3-Clausetailscale.com
Nebulaslackhq/nebulaMITdefined.net
ZeroTierzerotier/ZeroTierOneMPL 2.0docs.zerotier.com
NetBirdnetbirdio/netbirdBSD-3-Clause + AGPLv3(服务端)netbird.io
Headscalejuanfont/headscaleBSD-3-Clauseheadscale.net
OpenZitiopenziti/zitiApache 2.0openziti.io
Tincgsliepen/tincGPL-2.0tinc-vpn.org
libp2plibp2p/go-libp2pMITdocs.libp2p.io

协议说明:ZeroTier 核心代码采用 MPL 2.0(Mozilla Public License),而非早期文献中常引用的 BSL 1.1。NetBird 采用双许可模式:客户端与核心组件为 BSD-3-Clause,管理服务/signal/relay 等服务端组件为 AGPLv3。