对于UDP:一种是Cone NAT,简单说就是NAT每次分配给内网的端口是一样的,即使连接不同的主机,NAT上的端口还是不变(除非过期)。类似这样的NAT或防火墙,已经有比较可靠的解决方法:Peer-to-Peer (P2P) communication across middleboxes
另外一种是Symmetric NAT,对于这种NAT,内网连接不同的其他主机,NAT分配给它的是不同的端口。这种情况下,使用上面的方法就已经无效了。可能的办法就是猜测NAT下次将要分配的端口,也有算法可以比较准确得猜测到端口:Symmetric NAT Traversal using STUN
虽然还是没有办法完全猜对,但也是一种办法。我已经用实现Cone NAT,因为考虑到现在大部分的NAT不是Symmetric的,因此,就不去考虑Symmetric的实现了,也许日后有时间可以考虑。
对于TCP,也有办法:Establishing TCP Connections Between Hosts Behind NATs
TCP现在还没用上,估计很快就要用到了。
这里总结一下UDP穿透NAT:
对于Cone NAT,是这样的情况:
Server S1 Server S2
18.181.0.31:1235 138.76.29.7:1235
| |
| |
+----------------------+----------------------+
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 155.99.25.11:62000 v | v 155.99.25.11:62000 v
|
Cone NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 10.0.0.1:1234 v | v 10.0.0.1:1234 v
|
Client A
10.0.0.1:1234
下面是Symmetric NAT
Server S1 Server S2
18.181.0.31:1235 138.76.29.7:1235
| |
| |
+----------------------+----------------------+
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 155.99.25.11:62000 v | v 155.99.25.11:62001 v
|
Symmetric NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ | ^ Session 2 (A-S2) ^
| 18.181.0.31:1235 | | | 138.76.29.7:1235 |
v 10.0.0.1:1234 v | v 10.0.0.1:1234 v
|
Client A
10.0.0.1:1234
我实现的是Cone NAT,所有就说说Cone NAT:
Server S
|
|
+----------------------+----------------------+
| |
NAT A(NAip:NAport) NAT B(NBip:NBport)
| |
| |
Client A Client B
假设上图中的NATA和NATB都是Cone NAT。
现在ClientA要向ClientB发送数据(比如文件),首先ClientA和B分别通过他们的NATA和B在ServerS上登记,ServerS记录他们NAT的IP和端口号(因为是NAT和ServerS通信的,所以ServerS能也只能得到NAT的地址和端口号)。现在ClientA通过NATA告诉ServerS说要传文件给Client,ServerS就把ClientA的NATA的IP和端口号发送给NATB(因为B的NAT端口已经在ServerS上有记录了),ClientB收到后发送连接数据给NATA,这个时候NATA是不会接受ClientB发过来的包的,因为ClientA没有连接过B,NATA就不会接受B的数据了。虽然B连接A不成功,但是B已经连接过A了,NATB记住了,这时B告诉ServerS叫A过来连,A收到命令后连接B就成功了,于是建立连接,后面的传输就成功了!
整个过程如下:
ClientA-->NATA-->ServerS(A在S登记NAip:NAport)
ClientB-->NATB-->ServerS (B在S登记NBip:NBport)
ClientA-->NATA-->ServerS(A告诉S,要发数据给B)
ServerS-->NATB-->ClinetB(S把NAip:NAport发给B,并告诉B,A要发数据给你了)
ClientB-->NATB-->NATA(NAip:NAport)-->Dropped-->ClientA (B发的包别NATA丢掉了,A是收不到了)
ClientB-->NATB-->ServerS(B等不到A的回应,就告诉S,叫A过来连我吧)
ServerS-->NATA-->ClinetA(S把NBip:NBport发给A,并告诉A,B连不到你,你连B吧)
ClientA-->NATA-->NATB(NBip:NBport)-->ClinetB(B收到A的连接请求,要发送应答给A)
ClientB-->NATB-->NATA-->ClientA(A收到B的应答,后面可以发送数据给NBip:NBport了)
ClientA-->NATA-->NATB-->ClientB(现在开始就从这条路发数据了)
……
一般防火墙原理:
防火墙的设置分未界内根界外,就像以前使用的AtGuard,如果设置界内,就是对从外面发起的连接进行检测,如果规则设置阻挡,那么从外面访问进来的连接就被阻挡。但是,这不是说这样单方面的通信,很多人对这个不清楚,以为防火墙都不让外面进来了,那文件是怎么传的?其实,如果从里面发起连接,然后外面就可以连接进来了。虽然我没有关于防火墙确切的资料,但根据我的掌握的理论,我想我的推理是正确的。先由里面发起连接对方的请求,防火墙就会记住这个信息(也许是建立了一个session,一定时间内会释放或过期),对方收到请求,反过来连接防火墙是允许的。所以,对于上面的NAT原理,当然是可以用于一般的防火墙,而且更简单,因为得到的“NAT”的IP和端口都是本机的。
实现:
NAT信息的获取和发送:
理论上解决了,实现这个就简单了。本来做这个就是因为自己的客户端JBQ的需要,因此直接在JBQ客户端上面做。而服务器(用于p2p的服务器另外写)。原来的JBQ消息和文件都是通过服务器中转,现在就是把文件这部分改成p2p。
按照上面的原理,发送方需要把自己在NAT上的地址信息发送给接受方,然后由接受方向发送方连接,这样就可以在接受方的防火墙上面开个session,以让发送方进行后面的连接。但接受方连接不成功的时候,把自己的NAT信息再通过服务器发送给发送发送方,然后发送方再次连接接受方,于是建立连接。这里用到的服务器有两个,一个用来发送NAT信息,还有一个用来获取NAT信息。
本来这个两个应该是用同一个服务器,但是我们有原来的聊天服务器,因此,发送NAT信息的任务就交给聊天服务器,现在只要完成获取NAT信息的服务器就可以了。不管是Tcp还是UDP,连接双方都可以直接得到对方得IP和端口,因为只要由需要自己的NAT信息的客户端向p2p服务器发一个消息,p2p把跟它连接的客户端使用的NAT信息发回给客户端就可以。客户端收到p2p服务器发回来的消息以后,就可以把这个消息通过聊天服务器发给对方。
其他的握手和连接通过上面的理论都可以比较容易的写出来。
存在的问题:这种方法只适用于Cone NAT,对于Symmetric NAT还需要通过猜测端口。
另外,同样不适用于TCP,因为TCP的三次握手在这样的连接中必定失败。但也可以通过一点的办法解决,这个上面提到过。
总结:总的来说,上面用的方法可以解决的这一类NAT(防火墙的问题),可以让都处于NAT或防火墙后面的双方直接连接。上面的实现解决了我的JBQ客户端里面的一系列问题,视频等都已经在这个基础上实现p2p了。
总结:总的来说,上面用的方法可以解决的这一类NAT(防火墙的问题),可以让都处于NAT或防火墙后面的双方直接连接。上面的实现解决了我的JBQ客户端里面的一系列问题,视频等都已经在这个基础上实现p2p了。 另外,同样不适用于TCP,因为TCP的三次握手在这样的连接中必定失败。但也可以通过一点的办法解决,这个上面提到过。 存在的问题:这种方法只适用于Cone NAT,对于Symmetric NAT还需要通过猜测端口。 其他的握手和连接通过上面的理论都可以比较容易的写出来。 本来这个两个应该是用同一个服务器,但是我们有原来的聊天服务器,因此,发送NAT信息的任务就交给聊天服务器,现在只要完成获取NAT信息的服务器就可以了。不管是Tcp还是UDP,连接双方都可以直接得到对方得IP和端口,因为只要由需要自己的NAT信息的客户端向p2p服务器发一个消息,p2p把跟它连接的客户端使用的NAT信息发回给客户端就可以。客户端收到p2p服务器发回来的消息以后,就可以把这个消息通过聊天服务器发给对方。 按照上面的原理,发送方需要把自己在NAT上的地址信息发送给接受方,然后由接受方向发送方连接,这样就可以在接受方的防火墙上面开个session,以让发送方进行后面的连接。但接受方连接不成功的时候,把自己的NAT信息再通过服务器发送给发送发送方,然后发送方再次连接接受方,于是建立连接。这里用到的服务器有两个,一个用来发送NAT信息,还有一个用来获取NAT信息。 理论上解决了,实现这个就简单了。本来做这个就是因为自己的客户端JBQ的需要,因此直接在JBQ客户端上面做。而服务器(用于p2p的服务器另外写)。原来的JBQ消息和文件都是通过服务器中转,现在就是把文件这部分改成p2p。 NAT信息的获取和发送: 实现: 防火墙的设置分未界内根界外,就像以前使用的AtGuard,如果设置界内,就是对从外面发起的连接进行检测,如果规则设置阻挡,那么从外面访问进来的连接就被阻挡。但是,这不是说这样单方面的通信,很多人对这个不清楚,以为防火墙都不让外面进来了,那文件是怎么传的?其实,如果从里面发起连接,然后外面就可以连接进来了。虽然我没有关于防火墙确切的资料,但根据我的掌握的理论,我想我的推理是正确的。先由里面发起连接对方的请求,防火墙就会记住这个信息(也许是建立了一个session,一定时间内会释放或过期),对方收到请求,反过来连接防火墙是允许的。所以,对于上面的NAT原理,当然是可以用于一般的防火墙,而且更简单,因为得到的“NAT”的IP和端口都是本机的。 一般防火墙原理: