aa
前天编程做了一下UDP打洞的实验,今天特写了一篇文章总结一下。
我们知道网络上两个主机进行通信,如果其中一台主机拥有公网IP那么,那么进行会话是比较简单的,但是如果两台主机是位于不同内网之中的,那么应该如何进行通信呢。一种想法是再找一台公网的服务器,用来转发信息,但是这有一个问题,就是会给服务器带来压力,因此我们就来谈谈一种用于不同内网中的主机互相通信的一个解决方案——NAT打洞。
原理还是比较简单的,我们先了解一下什么是“NAT的洞”。当处于内网中的一台主机(ClientA/192.168.1.128)向一个公网的服务器(Server)发送数据时,这时NAT(NAT1)将会打开一个临时性的端口用于与公网的服务器进行通信,并且会把那个内网主机发送出的IP数据报的头部中源IP地址改为NAT的公网IP(218.7.32.28),将TCP或UDP数据报中源端口(2347)改为那个临时端口(26756)这样就实现了由“192.168.1.128:2347”到“218.7.32.28:26756”的源地址源端口的转换。这个数据包到达公网服务器后,服务器就可以根据这个包的头部信息进行回复。当服务器的数据包到达NAT后,NAT在将这个数据发送到内网主机192.168.1.128的2347端口。那么这个NAT上的26756端口我们就称作“洞”。如果这个NAT不是Full Cone NAT的话(其实大多数的NAT确实不是这种类型的),那么我们说这个“洞”是有方向性的。一个洞应该会指向一个(也可以是多个)公网主机的IP地址。比如上面说的例子,在NAT1上打的洞是指向Server的IP地址。来自其他公网主机发向这个洞(也就是218.7.32.28:26756)的数据包会被非Full Cone类型的NAT所丢弃。所以如果有另一台处于另一内网的主机(ClientB/192.168.0.5)向218.7.32.28:26756直接发送数据的话,同样也会被NAT1丢弃。
那么如何建立ClientA和ClientB的直接会话呢?
网络环境描述:
内网1NAT:NAT1/218.7.32.28
内网1中一台主机:ClientA/192.168.1.128
内网2NAT:NAT2/218.7.31.221
内网2中一台主机:ClientB/192.168.0.5
公网服务器:Server
首先让ClientA和ClientB登录到服务器Server(假如两台主机都采用2347端口),此时NAT1和NAT2会分别为ClientA和ClientB打开一个指向Server的洞(NAT1上218.7.32.28:26756和NAT2上218.7.31.221:27550)。服务器应改记录这两个客户端的信息(关键是那两个洞的信息)。当ClientA与ClientB要建立会话时,ClientA首先用2347端口向NAT2的洞发送一个数据包,当然这个数据包会被NAT2所丢弃,但是由于这是从NAT1内部向外部发送数据,所以NAT1为ClientA打开了一个指向NAT2的洞。而且这个新洞与原来NAT1上指向Server的旧洞的是同一个洞(因为是同一个端口26756),所以这里可以说这个洞具有了两个方向,同时指向Server和NAT2。这时ClientA应该通知Server,告诉ClientB,现在可以向NAT1的那个洞(218.7.32.28:26756)发送数据包了。当ClientB向NAT1的那个洞发送数据以后,NAT2也为ClientB打了一个指向NAT1的洞,这是可以说ClientA与ClientB的会话就建立完成了,他们可以不依赖Server进行通信了。如果以后ClientA和ClientB还需要建立其他会话 ,那么这个牵线的“媒人”可以不是Server,而可以是ClientA或ClientB了。
UDP打洞可以实现不同内网内的主机进行通信,而且实施性比较高,一般用于P2P通信。这也就是为什么常会看见腾讯QQ在开始传输文件时会显示“UDP连接已经建立”了。