当今互联网到处存在着一些中间件(MIddleBoxes
),如NAT
和防火墙
,导致两个(不在同一内网)中的客户端无法直接通信。 这些问题即便是到了IPV6
时代也会存在,因为即使不需要NAT
,但还有其他中间件
如防火墙
阻挡了链接的建立。 目前部署的中间件
多都是在C/S
架构上设计的,其中相对隐匿的客户机主
动向周知的服务端
(拥有静态IP
地址和DNS
名称)发起链接请求。 大多数中间件
实现了一种非对称的通讯模型,即内网中的主机可以初始化对外的链接
,而外网的主机却不能初始化对内网的链接, 除非经过中间件
管理员特殊配置。
在中间件
为常见的NAPT
的情况下(也是本文主要讨论的),内网中的客户端
没有单独的公网IP
地址, 而是通过NAPT
转换,和其他同一内网用户共享一个公网IP
。这种内网主机隐藏在中间件
后的不可访问性对于一些客户端软件如浏览器来说并不是一个问题,因为其只需要初始化对外的链接,从某方面来看反而还对隐私保护有好处。然而在P2P
应用中, 内网主机(客户端
)需要对另外的终端
(Peer
)直接建立链接,但是发起者
和响应者
可能在不同的中间件
后面, 两者都没有公网IP
地址。而外部对NAT
公网IP
和端口
主动的链接或数据都会因内网未请求被丢弃掉。
本文讨论的就是如何跨越NAT
实现内网主机直接通讯的问题。
网络模型
假设客户端A
和客户端B
的地址都是内网地址,且在不同的NAT
后面。A
、B
上运行的P2P
应用程序和服务器S
都使用了UDP
端口9982
,A
和B
分别初始化了 与Server
的UDP
通信,地址映射如图所示:
Server S
207.148.70.129:9981
|
|
+----------------------|----------------------+
| |
NAT A NAT B
120.27.209.161:6000 120.26.10.118:3000
| |
| |
Client A Client B
10.0.0.1:9982 192.168.0.1:9982
现在假设客户端A
打算与客户端B
直接建立一个UDP
通信会话。如果A
直接给B
的公网地址120.26.10.118:3000
发送UDP
数据,NAT B
将很可能会无视进入的 数据(除非是Full Cone NAT),因为源地址
和端口
与S
不匹配,而最初只与S
建立过会话。B
往A
直接发信息也类似。
假设A
开始给B
的公网地址发送UDP
数据的同时,给服务器S
发送一个中继
请求,要求B
开始给A
的公网地址发送UDP
信息。A
往B
的输出信息会导致NAT A
打开 一个A
的内网地址与与B
的外网地址之间的新通讯会话,B
往A
亦然。一旦新的UDP
会话在两个方向都打开之后,客户端A
和客户端B
就能直接通讯, 而无须再通过引导服务器S
了。
UDP
打洞技术有许多有用的性质。一旦一个的P2P
链接建立,链接的双方都能反过来作为“引导服务器”来帮助其他中间件后的客户端进行打洞, 极大减少了服务器的负载。应用程序不需要知道中间件
具体是什么(如果有的话),因为以上的过程在没有中间件
或者有多个中间件
的情况下 也一样能建立通信链路。
……