简单解释 P2P 技术之 UDP 打洞

3,964 阅读3分钟
Photo by Thomas Kvistholt on Unsplash

P2P 通信最大的障碍就是 NAT(网络地址转换),NAT 使得局域网内的设备也可以与公网进行通讯,但是不同 NAT 下的设备之间通讯将会变得很困难。UDP 打洞就是用来使得设备间绕过 NAT 进行通讯的一种技术。

简单解释 NAT

NAT 大家应该十分熟悉了,它分为几种。一种就叫做 NAT,它只对 IP 地址进行转换;另一种叫做 NAPT(Network Address/Port Translation),它可以对整个会话的端点(由 IP 地址和端口号组成)做转换,这是一种更加常见的 NAT 变种。

当然了,NAPT 也分为许多种,我们这里就不深入探讨了,大家如果有兴趣可以查阅相关的文献。

下面就简单介绍一下 NAT 的工作原理:

首先,NAT A 网下的设备 1(192.168.1.101)想与某公网 IP 通讯,设备 1 将包发给 NAT A,然后 NAT A 对源 IP 进行转换发给 NAT B(中间可能还会经过多重 NAT)。

这样做的目的是,NAT B 并不知晓 NAT A 下的各个设备,他只能与 NAT A 本身通讯,因此发送给 NAT B 的包源 IP 必须是 NAT A 的公网 IP,不然 NAT B 没有办法进行回复。

接下来 NAT B 将回复包再发回 NAT A,此时就是 NAT 发挥作用的时候了,NAT A 现在要做的就是将包再分发回之前的设备,如何确定要发给谁呢?NAT 中记录了一张表,之前 192.168.1.101 通过 2333 端口与 42.120.241.46 端口 443 通讯了,并且 NAT A 是用 60001 的端口转发出去的,那么这次接受到发往该 NAT 60001 端口的包时就应该再通过 2333 端口转发给 192.168.1.101。经过这样的过程,NAT A 下的设备都可以连接到互联网了!

UDP 打洞原理及过程

如上图所示,由于 NAT 的存在,当 NAT A 的设备 1 想与 NAT B 下的设备通讯时,必然要将目标 IP 设置为 NAT B 的公网地址,而 NAT B 转发表中并没有记录过 NAT A 与自身网络下设备的通讯记录,因此 NAT B 会将包丢掉。

下面我们来看看 UDP 打洞是怎么解决这个问题的。

为了能够进行 UDP 打洞,我们需要一台公网的服务器作为中转站,它是 NAT A 与 NAT B 之间的信使。

(为了方便起见,我们把地址为 192.168.1.101 的设备称为设备 1,把地址为 192.168.1.2 的设备称为设备 2,信使服务器称为 S)

首先,设备 1 和设备 2 都向 S 注册自己,S 中能记录各个设备此时使用的公网 IP 地址和端口号,例如设备 1 是 123.122.53.20:31000,设备 2 是 42.120.241.46:41000。

然后设备 1 与设备 2 都向 S 获取对方的公网 IP 与之前预留的端口号,就像这样:

然后就是最关键的一步,打洞。

设备 1 向 42.120.241.46:41000 发一个包,NAT B 自然能接收到这个包,然而它不知道来自 NAT A 的包应该发给谁,因此 NAT B 将这个包舍弃。但是由于设备 1 向 42.120.241.46:41000 发过包,NAT A 会记录:以后来自 42.120.241.46:41000 的包都发给设备1

设备 2 也做相同的操作,让 NAT B 也知道:以后来自 123.122.53.20:31000 的包都发给设备 2

至此,NAT A 与 NAT B 都互相为对方保留了端口,就可以愉快地通讯了。

当然了,大致原理是很简单的,实际操作起来情况可能会更复杂,会涉及到丢包、多重 NAT 等问题的处理,本文仅起到一个科普解释的作用。


Reference:
Peer-to-Peer Communication Across Network Address Translators