本文由 愚人猫(Idiomeo) 编写
你可以去查看我的博客原文
一.为什么需要打洞技术?
在当今的网络环境中,大多数设备都位于网络地址转换 (NAT) 设备之后,这导致了一个普遍存在的问题:如何让位于不同 NAT 设备后的两个设备直接建立通信? 这个问题在 P2P 应用中尤为突出,如在线游戏、视频会议、文件共享等场景都需要设备之间的直接通信。
传统的 C/S 架构应用中,客户端可以主动向服务器发起连接,但反过来却不行。这是因为 NAT 设备会阻止来自公网的未经请求的连接尝试。然而,在 P2P 应用中,我们需要两个客户端之间能够直接通信,这就需要突破 NAT 的限制,这就是 P2P 打洞技术所要解决的核心问题。
二.NAT 类型与工作原理
NAT 的基本概念与作用
网络地址转换 (NAT) 是一种将私有网络地址 (如 192.168.1.0/24) 转换为公网地址的技术。它的主要作用是节约公网 IP 地址资源,使得多个私有网络设备可以共享一个公网 IP 地址访问互联网。
在 NAT 设备中,维护着一个映射表,记录了私有 IP 地址和端口到公网 IP 地址和端口的映射关系。当内部设备向外部发送数据时,NAT 设备会将数据包的源 IP 和端口替换为自己的公网 IP 和一个可用端口,并在映射表中记录这一转换。当外部设备返回响应时,NAT 设备根据映射表将数据包转发给对应的内部设备。
NAT 设备通常位于家庭或企业网络的边界,作为内部网络与公网之间的网关。它的存在使得外部设备无法直接访问内部设备,这给 P2P 通信带来了挑战。
NAT 类型及其对通信的影响
根据 NAT 设备的行为特性,可以将其分为四种主要类型:完全圆锥型 (Full Cone)、限制圆锥型 (Restricted Cone)、端口限制圆锥型 (Port Restricted Cone) 和对称型 (Symmetric)。不同类型的 NAT 对 P2P 通信的影响各不相同。
完全圆锥型 NAT
完全圆锥型 NAT 是最开放的 NAT 类型。在这种 NAT 下,一旦内部设备的某个端口被映射到公网地址的某个端口,任何外部设备都可以向这个公网端口发送数据,NAT 设备会将数据转发给对应的内部设备,而不管这些数据来自哪个外部地址。
数学描述:设内部地址为 (iAddr, iPort),映射到公网地址 (eAddr, ePort)。对于任意外部地址 (aAddr, aPort),如果外部设备向 (eAddr, ePort)发送数据,NAT 设备会将其转发给 (iAddr, iPort)。
这种类型的 NAT 对 P2P 通信最为友好,因为一旦映射建立,两个设备之间可以直接通信。
限制圆锥型 NAT
限制圆锥型 NAT 比完全圆锥型 NAT 更严格。在这种 NAT 下,只有当内部设备已经向某个外部 IP 地址发送过数据后,该外部 IP 地址才能向内部设备的映射端口发送数据,但可以是任意端口。
数学描述:设内部地址为 (iAddr, iPort),映射到公网地址 (eAddr, ePort)。对于外部地址 (aAddr, aPort),只有当内部设备已经向 aAddr发送过数据时,NAT 设备才会将来自 (aAddr, aPort)的数据转发给 (iAddr, iPort)。
这种类型的 NAT 允许外部设备与内部设备通信,但仅限于内部设备已经通信过的 IP 地址,而不考虑端口。
端口限制圆锥型 NAT
端口限制圆锥型 NAT 是更严格的一种类型。在这种 NAT 下,只有当内部设备已经向某个外部 IP 地址的特定端口发送过数据后,该外部 IP 地址的该特定端口才能向内部设备的映射端口发送数据。
数学描述:设内部地址为 (iAddr, iPort),映射到公网地址 (eAddr, ePort)。对于外部地址 (aAddr, aPort),只有当内部设备已经向 (aAddr, aPort)发送过数据时,NAT 设备才会将来自 (aAddr, aPort)的数据转发给 (iAddr, iPort)。
这种类型的 NAT 对通信的限制更加严格,要求外部设备的 IP 和端口都必须是内部设备已经通信过的。
对称型 NAT
对称型 NAT 是最严格的一种类型。在这种 NAT 下,内部设备每次向不同的外部 IP 地址或端口发送数据时,NAT 设备都会创建一个新的映射。此外,只有来自该特定外部 IP 地址和端口的数据才能被转发回内部设备。
数学描述:设内部地址为 (iAddr, iPort)。当内部设备向 (aAddr1, aPort1)发送数据时,NAT 设备会创建一个映射 (eAddr1, ePort1)。当内部设备向 (aAddr2, aPort2)发送数据时,NAT 设备会创建另一个映射 (eAddr2, ePort2),即使 aAddr1 == aAddr2但 aPort1 != aPort2。对于外部地址 (aAddr, aPort),只有当内部设备已经向 (aAddr, aPort)发送过数据时,NAT 设备才会将来自 (aAddr, aPort)的数据转发给 (iAddr, iPort)。
这种类型的 NAT 使得 P2P 通信变得非常困难,因为两个设备之间很难建立直接的连接。
NAT 类型对 P2P 通信的影响总结
不同类型的 NAT 对 P2P 通信的支持程度各不相同:
| NAT 类型 | P2P 通信支持度 | 直接通信可能性 |
|---|---|---|
| 完全圆锥型 | 高 | 容易 |
| 限制圆锥型 | 中等 | 可能 |
| 端口限制圆锥型 | 低 | 困难 |
| 对称型 | 极低 | 几乎不可能 |
在实际应用中,大多数家用路由器使用完全圆锥型或限制圆锥型 NAT,而企业级路由器可能使用更严格的类型。了解 NAT 的类型对于实现可靠的 P2P 通信至关重要。
三.UDP 打洞原理与实现
UDP 打洞的基本原理
UDP 打洞是实现 P2P 通信的常用方法,其基本原理是利用 NAT 设备的特性,通过中间服务器的协助,在两个客户端的 NAT 设备上建立映射关系,使得它们能够直接通信。
UDP 打洞的核心思想是:即使两个客户端都位于 NAT 之后,只要它们能够同时向对方的公网地址发送数据,它们的 NAT 设备就会建立相应的映射,从而允许后续的数据直接通过。
具体来说,UDP 打洞的过程如下:
- 客户端 A 和客户端 B 分别向中间服务器 S 发送数据,服务器 S 记录下它们的公网地址
(A_public, A_port)和(B_public, B_port)。 - 服务器 S 将 B 的公网地址告诉 A,将 A 的公网地址告诉 B。
- 客户端 A 向 B 的公网地址发送一个 UDP 数据包,客户端 B 向 A 的公网地址发送一个 UDP 数据包。
- 由于这两个数据包是主动发送的,它们的 NAT 设备会建立相应的映射,允许后续的数据通过。
- 一旦映射建立,客户端 A 和 B 就可以直接通信了。
需要注意的是,第二步中客户端 A 和 B 发送的初始数据包可能会被对方的 NAT 设备丢弃,但这并不影响,因为这两个数据包的主要目的是在各自的 NAT 设备上建立映射关系,而不是实际传输数据。
UDP 打洞的数学模型
为了更好地理解 UDP 打洞的原理,我们可以建立一个数学模型。
假设客户端 A 的内网地址为 A_private,映射到公网地址 A_public;客户端 B 的内网地址为 B_private,映射到公网地址 B_public。中间服务器 S 的地址为 S_addr。
在打洞过程中,我们需要解决以下问题:
- 如何让 A 和 B 获取对方的公网地址?
- 如何让 A 和 B 的 NAT 设备允许对方的数据通过?
数学上,我们可以将这个问题描述为:找到一种方式,使得对于客户端 A 和 B,有:
A _ p u b l i c ∈ N A T _ B ( B _ p r i v a t e ) A\_public ∈ NAT\_B(B\_private) A_public∈NAT_B(B_priva

2664

被折叠的 条评论
为什么被折叠?



