文章
iroh在NAT环境下建立P2P连接的完整流程
目录
一、参与角色#
| 角色 | 描述 |
|---|---|
| A | Provider / Sender,提供服务的一方 |
| B | Receiver / Client,发起连接的一方 |
| R | Relay / 信令中继,公网可达,帮助节点互相发现和穿透 NAT |
所有节点都是 iroh Endpoint 的实例,Relay 是特殊的公网 Endpoint。
二、阶段 0:节点上线#
- Endpoint 创建
let endpoint = Endpoint::builder().bind().await?;
- Endpoint 内部动作
- 监听本地端口(UDP / QUIC)
- 向 Relay 建立出站连接(NAT 允许)
- Relay 记录 NodeId 与可达性信息
✅ NAT 不会阻止出站连接,因此注册总是成功的
三、阶段 1:生成 ticket#
- ticket 的作用:告诉对方节点如何找到自己
- ticket 内容:
- NodeId(身份标识)
- 可选的直连地址
- Relay 提示
- 身份认证信息
🔑 ticket 并非一次性,它描述的是 Endpoint 的可达性和身份,只要 Endpoint 活着,就可以多次使用。
四、阶段 2:ticket 的传递#
- 方式(不依赖 iroh 网络层):
- 二维码、IM、HTTP API、剪贴板、邮件等
- 目的:让 B 得到 A 的 NodeId 与 Relay 信息
这是信令层行为,不涉及 P2P 数据传输
五、阶段 3:B 使用 ticket 连接 A#
let conn = endpoint.connect(ticket).await?;
Endpoint 内部执行:
- 从 ticket 获取 A 的 NodeId 和 Relay 地址
- 先连接 Relay(出站连接)
- 向 Relay 说:“我想找 NodeId = A”
- Relay 交换 A ↔ B 的可达信息
✅ 通过 Relay,B 能找到 A 的网络位置,即使在 NAT 后
六、阶段 4:尝试 NAT 穿透(UDP 打洞)#
- 双方同时向对方地址发送 UDP 包
- NAT 会为临时端口打开映射
- 数据包在 NAT 中相遇 → 建立直连
成功#
- 建立 P2P 直连
- Relay 退出数据路径
失败(对称 NAT 等)#
- Relay 继续转发数据
- 应用层无感知
七、阶段 5:直连 / Relay fallback 数据传输#
- 直连成功:低延迟、高性能 P2P
- 直连失败:Relay 做中继,保证通信可靠性
- Endpoint 层面:统一管理连接,应用无需关心底层路径选择
八、直觉类比#
- Relay = 社交中介
- 你想联系朋友,但不知道他在哪
- 先通过中介介绍
- 中介帮助尝试直连
- 直连失败 → 中介继续转发消息
- ticket = 名片
- 告诉别人你的身份和可达性
- Endpoint = 网络化身份
- 包含身份、可达性、连接管理
- 自动选择直连或 Relay
九、整合一句话总结#
iroh 的 P2P 连接流程:ticket 提供身份信息 → Relay 协助发现 → 双方尝试 NAT 打洞 → 直连成功则 P2P,失败则 Relay fallback → Endpoint 管理整个过程。