内网穿透即NAT穿透,网络连接时术语,计算机是局域网内时,外网与内网的计算机节点需要连接通信,有时就会出现不支持内网穿透。
创新互联建站专注于企业营销型网站、网站重做改版、港北网站定制设计、自适应品牌网站建设、HTML5建站、商城开发、集团公司官网建设、外贸网站制作、高端网站制作、响应式网页设计等建站业务,价格优惠性价比高,为港北等各大城市提供网站开发制作服务。
就是说映射端口,能让外网的电脑找到处于内网的电脑,提高下载速度。
不管是内网穿透还是其他类型的网络穿透,都是网络穿透的统一方法来研究和解决。在百科词条
NAT穿越,nat穿透中有关于网络穿透的详细信息。
NAT,即NAT Traversal,可译为网络地址转换或网络地址翻译。
NAT英文全称是“Network Address Translation”,中文意思是“网络地址转换”,它是一个IETF(Internet Engineering Task Force, Internet工程任务组)标准,允许一个整体机构以一个公用IP(Internet Protocol)地址出现在Internet上。顾名思义,它是一种把内部私有网络地址(IP地址)翻译成合法网络IP地址的技术。
简单的说,NAT就是在局域网内部网络中使用内部地址,而当内部节点要与外部网络进行通讯时,就在网关(可以理解为出口,打个比方就像院子的门一样)处,将内部地址替换成公用地址,从而在外部公网(internet)上正常使用,NAT可以使多台计算机共享Internet连接,这一功能很好地解决了公共IP地址紧缺的问题。通过这种方法,您可以只申请一个合法IP地址,就把整个局域网中的计算机接入Internet中。这时,NAT屏蔽了内部网络,所有内部网计算机对于公共网络来说是不可见的,而内部网计算机用户通常不会意识到NAT的存在。
nat穿透:在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接
内网穿透即是使用公网服务器作为代理,转发内网(如办公室、家里)的网络请求使其能够在外网中被访问到。
server端监听两个端口,一个用来和接收用户的http请求,一个监听gRPC客户端,和内网服务器进行通信;
client启动时连接server端;
当User请求server http端口时,将http进行阻塞,并将User请求内容通过gRPC发给client;
client将从server收到的请求发往本地的http服务;
client将从本地程序收到的http response通过gRPC发送给server;
server结束http阻塞,将从client收到的http response发给User。
github地址:
原文链接:
一、什么是NAT?为什么要使用NAT?
NAT是将私有地址转换为合法IP地址的技术,通俗的讲就是将内网与内网通信时怎么将内网私有IP地址转换为可在网络中传播的合法IP地址。NAT的出现完美地解决了lP地址不足的问题,而且还能够有效地避免来自网络外部的攻击,隐藏并保护网络内部的计算机。
二、NAT的分类
STUN标准中,根据内部终端的地址(LocalIP:LocalPort)到NAT出口的公网地址(PublicIP:PublicPort)的影射方式,把NAT分为四种类型:
1、Full Cone NAT: 内网主机建立一个socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,任何外部主机只要知道这个(PublicIP:PublicPort)就可以发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
2、Restricted Cone NAT: 内网主机建立一个socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机IP发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,任何端口)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
3、Port Restricted Cone NAT: 内网主机建立一个socket(LocalIP:LocalPort) 第一次使用这个socket给外部主机发送数据时NAT会给其分配一个公网(PublicIP:PublicPort),以后用这个socket向外面任何主机发送数据都将使用这对(PublicIP:PublicPort)。此外,如果任何外部主机想要发送数据给这个内网主机,只要知道这个(PublicIP:PublicPort)并且内网主机之前用这个socket曾向这个外部主机(IP,Port)发送过数据。只要满足这两个条件,这个外部主机就可以用自己的(IP,Port)发送数据给(PublicIP:PublicPort),内网的主机就能收到这个数据包。
4、Symmetric NAT: 内网主机建立一个socket(LocalIP,LocalPort),当用这个socket第一次发数据给外部主机1时,NAT为其映射一个(PublicIP-1,Port-1),以后内网主机发送给外部主机1的所有数据都是用这个(PublicIP-1,Port-1),如果内网主机同时用这个socket给外部主机2发送数据,NAT会为其分配一个(PublicIP-2,Port-2), 以后内网主机发送给外部主机2的所有数据都是用这个(PublicIP-2,Port-2).如果NAT有多于一个公网IP,则PublicIP-1和PublicIP-2可能不同,如果NAT只有一个公网IP,则Port-1和Port-2肯定不同,也就是说一定不能是PublicIP-1等于 PublicIP-2且Port-1等于Port-2。此外,如果任何外部主机想要发送数据给这个内网主机,那么它首先应该收到内网主机发给他的数据,然后才能往回发送,否则即使他知道内网主机的一个(PublicIP,Port)也不能发送数据给内网主机,这种NAT无法实现P2P通信,但是如果另一方是Full Cone NAT,还是可以实现穿透的,下面我会详细分析各种类型NAT穿透的情况。
NAT 功能通常被集成到路由器、防火墙、ISDN路由器或者单独的NAT设备中。所以我们大家很少会知道NAT,上面NAT类型的概念描述是比较通俗的,但为了更便于理解,我再举例阐述一下NAT的原理。
现有通信的双方A和B,当A和B都是在公网的时候,通信是不用NAT的。假设A在内网,内网IP是192.168.1.3,端口号是5000,A经过NAT后的IP是221.221.221.100,端口号是8000,B的IP是202.105.124.100,端口是8500。如果B要去主动连接A,即使B知道A经过NAT后的IP和端口也是无法连接成功的,因为A没有向B(202.105.124.100:8500)发送过数据,所以B的数据包会被A的NAT丢弃,于是连接失败。但是A如果去主动连接B,由于B是在公网,所以会连接成功,通信也就会建立。这也就是反弹连接木马“反弹”二字的精髓。
当客户端A和B都是处在内网的时候,双方由于都不知道对方的公网IP和端口,就会无从下手,所以要在客户端A和B之间架设一台服务器S来为它们牵线,而且S是处在公网,以保证A和B都能连接到S。客户端A和B登录时都首先连接S,S就会知道A和B经过NAT后的IP和端口,当A想要连接B时,就像S发出请求,S会把B经过NAT后的IP和端口告诉A,同时S向B发送A经过NAT后的IP和端口,并要求B发送数据给A,B发送数据到达A时会被A的NAT抛弃,但是B的NAT会有B发送数据到A的记录,这是A再向B发送数据时就会被B的NAT放行,因为B曾经向A的外网IP和端口发送过数据。可能有点乱,下面以故事的形式叙述一下这个情景。
人物:A(男) NAT_A(A家接线员) B(女) NAT_B (B家接线员) S
场景介绍:A想认识B,但是不知道B的电话,S跟A、B都是朋友,并且知道A和B的电话。接线员的职责:对往外转接的电话不做询问,对往内转接的电话则要过滤以免有骚扰电话。过滤规则:在一定时间内没有拨打过的号码就过滤。
首先A给S打电话:
A说:我想认识你朋友B,你把她电话给我呗。
S说:行,她的电话是PublicIP_B,我让她先给你打个电话,要不她家接线员不帮你转接。
A说:好。
S跟B打电话:
S说:我有一个朋友A,人挺好的,他想认识你,你给他打个电话,他的电话号码是PublicIP_A。
B说:行,打完告诉你。
S说:好的。
B打电话到A家,B家接线员NET_B看到女主人想往PublicIP_A打电话就转接到A家了,同时把号码PublicIP_A记录下来,A家接线员NAT_A一看号码是个近期没打过的号,就给挂断了。
B给S打电话:
B说:我打完电话了
S说:好,等着吧,一会他就给你打进来了。
S给A打电话:
S说:他给你打完电话了,你快点给她打。
A打电话到B家, A家接线员NET_A看到男主人想往PublicIP_B打电话就转接到B家了,B家接线员NET_B看到是刚刚拨过的PublicIP_A号码打过来的,就转接给B了,A和B的电话也就打通了。
A和B通话:
A说:电话终于打通了,想认识你挺困难的。
B说:是啊。
∶
∶
以上虽然和实际不太一样,但穿透的整体过程基本就是这样。A往B发送数据的唯一阻碍就是NET_B,所以想要成功发送数据,必须把NET_B穿一个洞,A是无法完成这项工作的,所以就得让B完成这个打洞操作,也就是让B往A发送数据,这样NET_B就会误以为A发送的数据是上次会话的一部分从而不予阻拦。
但是,由于NAT的类型没有一个统一的标准,所以NAT穿透使用的技术有很多种,穿透的成功率也不一样。还有些NAT类型的内网之间几乎无法穿透。下面我们用实例详细分析一下各种NAT类型穿透的可行性。
A机器在私网(192.168.0.3)
A侧NAT服务器(221.221.221.100)
B机器在另一个私网(192.168.0.5)
B侧NAT服务器(210.30.224.70)
C机器在公网(210.202.14.36)作为A和B之间的中介
A机器连接C机器,假使是A(192.168.0.3:5000)- A侧NAT(转换后221.221.221.100:8000)- C(210.202.14.36:2000)
B机器也连接C机器,假使是B(192.168.0.5:5000)- B侧NAT(转换后210.30.224.70:8000)- C(210.202.14.36:2000)
A机器连接过C机器后,A向C报告了自己的内部地址(192.168.0.3:5000),此时C不仅知道了A的外部地址(C通过自己看到的221.221.221.100:8000)也知道了A的内部地址。同理C也知道了B的外部地址(210.30.224.70:8000)和 内部地址(192.168.0.5:5000)。之后,C作为中介,把A的两个地址告诉了B,同时也把B的两个地址告诉了A。
假设A先知道了B的两个地址,则A从192.168.0.3:5000处同时向B的两个地址192.168.0.5:5000和210.30.224.70:8000发包,由于A和B在两个不同的NAT后面,故从A(192.168.0.3:5000)到B(192.168.0.5:5000)的包肯定不通,现在看A(192.168.0.3:5000)到B(210.30.224.70:8000)的包,分如下两种情况:
1、B侧NAT属于Full Cone NAT
则无论A侧NAT属于Cone NAT还是Symmetric NAT,包都能顺利到达B。如果程序设计得好,使得B主动到A的包也能借用A主动发起建立的通道的话,则即使A侧NAT属于Symmetric NAT,B发出的包也能顺利到达A。
结论1:只要单侧NAT属于Full Cone NAT,即可实现双向通信。
2、B侧NAT属于Restricted Cone或Port Restricted Cone
则包不能到达B。再细分两种情况
(1)、A侧NAT属于Restricted Cone或Port Restricted Cone
虽然先前那个初始包不曾到达B,但该发包过程已经在A侧NAT上留下了足够的记录:A(192.168.0.3:5000)-(221.221.221.100:8000)-B(210.30.224.70:8000)。如果在这个记录没有超时之前,B也重复和A一样的动作,即向A(221.221.221.100:8000)发包,虽然A侧NAT属于Restricted Cone或Port Restricted Cone,但先前A侧NAT已经认为A已经向B(210.30.224.70:8000)发过包,故B向A(221.221.221.100:8000)发包能够顺利到达A。同理,此后A到B的包,也能顺利到达。
结论2:只要两侧NAT都不属于Symmetric NAT,也可双向通信。换种说法,只要两侧NAT都属于Cone NAT,即可双向通信。
(2)、A侧NAT属于Symmetric NAT
因为A侧NAT属于Symmetric NAT,且最初A到C发包的过程在A侧NAT留下了如下记录:A(192.168.0.3:5000)-(221.221.221.100:8000)- C(210.202.14.36:2000),故A到B发包过程在A侧NAT上留下的记录为:
A(192.168.0.3:5000)-(221.221.221.100:8001)-B(210.30.224.70:8000)(注意,转换后端口产生了变化)。而B向A的发包,只能根据C给他的关于A的信息,发往A(221.221.221.100:8000),因为A端口受限,故此路不通。再来看B侧NAT,由于B也向A发过了包,且B侧NAT属于Restricted Cone或Port Restricted Cone,故在B侧NAT上留下的记录为:B(192.168.0.5:5000)-(210.30.224.70:8000)-A(221.221.221.100:8000),此后,如果A还继续向B发包的话(因为同一目标,故仍然使用前面的映射),如果B侧NAT属于Restricted Cone,则从A(221.221.221.100:8001)来的包能够顺利到达B;如果B侧NAT属于Port Restricted Cone,则包永远无法到达B。
结论3:一侧NAT属于Symmetric NAT,另一侧NAT属于Restricted Cone,也可双向通信。
反过来想,则可以得出另一个结论:两个都是Symmetric NAT或者一个是Symmetric NAT、另一个是Port Restricted Cone,则不能双向通信,因为NAT无法穿透。
上面的例子虽然只是分析了最初发包是从A到B的情况,但是,由于两者的对称性,前面得出的几条结论没有方向性,双向都适用。
我们上面得出了四条结论,natcheck网站则把他归结为一条:只要两侧NAT都属于Cone NAT(含Full Cone、Restricted Cone和Port Restricted Cone三者),即可双向通信。没有把我们的结论3包括进去。
一般情况下,只有比较注重安全的大公司会使用Symmetric NAT,禁止使用P2P类型的通信,很多地方使用的都是Cone NAT,因此穿透技术还是有发展前景的。
三、使用UDP、TCP穿透NAT
上面讲的情况可以直接应用于UDP穿透技术中,使用TCP 协议穿透NAT 的方式和使用UDP 协议穿透NAT 的方式几乎一样,没有什么本质上的区别,只是将无连接的UDP 变成了面向连接的TCP 。值得注意是:
1、 B在向A打洞时,发送的SYN 数据包,而且同样会被NAT_A 丢弃。同时,B需要在原来的socket 上监听,由于重用socket ,所以需要将socket 属性设置为SO_REUSEADDR 。
A向B发送连接请求。同样,由于B到A方向的孔已经打好,所以连接会成功,经过3 次握手后,A到B之间的连接就建立起来了。具体过程如下:
1、 S启动两个网络侦听,一个叫【主连接】侦听,一个叫【协助打洞】的侦听。
2、 A和B分别与S的【主连接】保持联系。
3、 当A需要和B建立直接的TCP连接时,首先连接S的【协助打洞】端口,并发送协助连接申请。同时在该端口号上启动侦听。注意由于要在相同的网络终端上绑定到不同的套接字上,所以必须为这些套接字设置 SO_REUSEADDR 属性(即允许重用),否则侦听会失败。
4、 S的【协助打洞】连接收到A的申请后通过【主连接】通知B,并将A经过NAT-A转换后的公网IP地址和端口等信息告诉B。
5、 B收到S的连接通知后首先与S的【协助打洞】端口连接,随便发送一些数据后立即断开,这样做的目的是让S能知道B经过NAT-B转换后的公网IP和端口号。
6、 B尝试与A的经过NAT-A转换后的公网IP地址和端口进行connect,大多数路由器对于不请自到的SYN请求包直接丢弃而导致connect失败,但NAT-B会纪录此次连接的源地址和端口号,为接下来真正的连 接做好了准备,这就是所谓的打洞,即B向A打了一个洞,下次A就能直接连接到B刚才使用的端口号了。
7、 客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪以后通过与S的【主连接】回复消息“我已经准备好”,S在收到以后将B经过NAT-B转换后的公网IP和端口号告诉给A。
8、 A收到S回复的B的公网IP和端口号等信息以后,开始连接到B公网IP和端口号,由于在步骤6中B曾经尝试连接过A的公网IP地址和端口,NAT-B纪录了此次连接的信息,所以当A主动连接B时,NAT-B会认为是合法的SYN数据,并允许通过,从而直接的TCP连接建立起来了。
参考网址:
holer 轻量级的内网穿透工具,holer服务端采用Java语言实现,服务端界面漂亮简洁。
Holer客户端采用了Java语言和GO语言实现了两种版本,支持几乎所有的OS平台。
用到流行的微服务框架springboot和Java网络框架netty。
配置很简单,针对所有TCP协议只需在客户端设置一个holer access key即可。
采用MySQL数据库统一管理数据。
NAT有两大类,基本NAT和NAPT。
静态NAT:一个公网IP对应一个内部IP,一对一转换
动态NAT:N个公网IP对应M个内部IP,不固定的一对一转换关系
现在基本使用这种,又分为对称和锥型NAT。
锥型NAT ,有完全锥型、受限制锥型、端口受限制锥型三种:
对称NAT :
把所有来自相同内部IP地址和端口号,到特定目的IP地址和端口号的请求映射到相同的外部IP地址和端口。如果同一主机使用不同的源地址和端口对,发送的目的地址不同,则使用不同的映射。只有收到了一个IP包的外部主机才能够向该内部主机发送回一个UDP包。对称的NAT不保证所有会话中的(私有地址,私有端口)和(公开IP,公开端口)之间绑定的一致性。相反,它为每个新的会话分配一个新的端口号。
对称NAT是一个请求对应一个端口,非对称NAT是多个请求对应一个端口(象锥形,所以叫Cone NAT)。
连接服务器为A,NAT检测服务器为B。
第一步:当一个接收客户端(Endpoint-Receiver ,简称 EP-R)需要接收文件信息时,在其向连接服务器发送文件请求的同时紧接着向检测服务器发送NAT检测请求。此处再次强调是“紧接着”,因为对于对称型NAT来说,这个操作可以直接算出其地址分配的增量(⊿p)。
第二步:当EP-R收到A或B的反馈信息时发现其外部地址与自身地址不同时就可以确定自己在NAT后面;否则,就是公网IP。
第三步:由服务器A向B发送其获得的EP-R的外部映射地址(IPa/Porta),服务器B获得后进行比较,如果端口不同,则说明这是对称型NAT,同时可以直接计算出其分配增量:
⊿p=Portb-Porta
第四步:如果端口号相同,则由B向EP-R的Porta发送连接请求,如果EP-R有响应,则说明EP-R没有IP和Port的限制,属于全ConeNAT类型。
第五步:如果没有响应,则由服务器B使用其新端口b’向EP-R的Portb端口发送连接请求,如果有响应,则说明EP-R只对IP限制,属于限制性ConeNAT类型;否则就是对IP和port都限制,属于端口限制性ConeNAT类型。
通过上述五步基本可以全部检测出EP-R是否在公网,还是在某种NAT后面。
这也是一项可选配置任务,可根据需要为NAT 地址映射表配置老化时间,以控制用户对NAT 配置的使用,确保内、外网的通信安全。
配置NAT 地址映射表项老化时间的方法也很简单,只须在系统视图下使用firewall-nat session { dns | ftp | ftp-data | http | icmp | tcp | tcp-proxy | udp | sip | sip-media | rtsp |rtsp-media }aging-time time-value 命令配置即可。参数 time-value的取值范围为1~65 535的整数秒。如果要配置多个会话表项的超时时间需要分别用本命令配置。
缺省情况下,各协议的老化时间为:DNS(120 s)、ftp(120 s)、ftp-data(120 s)、HTTP(120 s)、icmp(20 s)、tcp(600 s)、tcp-proxy(10 s)、udp(120 s)、sip(1 800 s)、sip-media ( 120 s )、rtsp ( 60 s )、rtsp-media ( 120 s ), 可用undo firewall-natsession { all | dns | ftp | ftp-data | http | icmp | tcp | tcp-proxy | udp | sip | sip-media | rtsp |rtsp-media } aging-time 命令恢复对应会话表项的超时时间为缺省值。
1、 中间服务器保存信息、并能发出建立UDP隧道的命令
2、 网关均要求为Cone NAT类型。Symmetric NAT不适合。
3、 完全圆锥型网关可以无需建立udp隧道,但这种情况非常少,要求双方均为这种类型网关的更少。
4、 假如X1网关为Symmetric NAT, Y1为Address Restricted Cone NAT 或Full Cone NAT型网关,各自建立隧道后,A1可通过X1发送数据报给Y1到B1(因为Y1最多只进行IP级别的甄别),但B2发送给X1的将会被丢弃(因为发送来的数据报中端口与X1上存在会话的端口不一致,虽然IP地址一致),所以同样没有什么意义。
5、 假如双方均为Symmetric NAT的情形,新开了端口,对方可以在不知道的情况下尝试猜解,也可以达到目的,但这种情形成功率很低,且带来额外的系统开支,不是个好的解决办法。
6、 不同网关型设置的差异在于,对内会采用替换IP的方式、使用不同端口不同会话的方式,使用相同端口不同会话的方式;对外会采用什么都不限制、限制IP地址、限制IP地址及端口。
7、 这里还没有考虑同一内网不同用户同时访问同一服务器的情形,如果此时网关采用AddressRestricted Cone NAT 或Full Cone NAT型,有可能导致不同用户客户端可收到别人的数据包,这显然是不合适的。
为什么网上讲到的P2P打洞基本上都是基于UDP协议的打洞?难道TCP不可能打洞?还是TCP打洞难于实现?
假设现在有内网客户端A和内网客户端B,有公网服务端S。
如果A和B想要进行UDP通信,则必须穿透双方的NAT路由。假设为NAT-A和NAT-B。
S也和A B 分别建立了会话,由S发到NAT-A的数据包会被NAT-A直接转发给A,
由S发到NAT-B的数据包会被NAT-B直接转发给B,除了S发出的数据包之外的则会被丢弃。
所以:现在A B 都能分别和S进行全双工通讯了,但是A B之间还不能直接通讯。
并转发给A了(即B现在能访问A了);再由S命令B向A的公网IP发送一个数据包,则
NAT-B能接收来自NAT-A的数据包并转发给B了(即A现在能访问B了)。
以上就是“打洞”的原理。
pre style="margin: 0px; padding: 0px; white-space: pre-wrap; overflow-wrap: break-word;"为了保证A的路由器有与B的session,A要定时与B做心跳包,同样,B也要定时与A做心跳,这样,双方的通信通道都是通的,就可以进行任意的通信了。/pre
API造成的。
UDP的socket允许多个socket绑定到同一个本地端口,而TCP的socket则不允许。
这是这样一个意思:A B要连接到S,肯定首先A B双方都会在本地创建一个socket,
去连接S上的socket。创建一个socket必然会绑定一个本地端口(就算应用程序里面没写
端口,实际上也是绑定了的,至少java确实如此),假设为8888,这样A和B才分别建立了到
S的通信信道。接下来就需要打洞了,打洞则需要A和B分别发送数据包到对方的公网IP。但是
问题就在这里:因为NAT设备是根据端口号来确定session,如果是UDP的socket,A B可以
分别再创建socket,然后将socket绑定到8888,这样打洞就成功了。但是如果是TCP的
socket,则不能再创建socket并绑定到8888了,这样打洞就无法成功。
**UDP打洞**的过程大致如此:
1、双方都通过UDP与服务器通讯后,网关默认就是做了一个外网IP和端口号 与你内网IP与端口号的映射,这个无需设置的,服务器也不需要知道客户的真正内网IP
2、用户A先通过服务器知道用户B的外网地址与端口
3、用户A向用户B的外网地址与端口发送消息,
4、在这一次发送中,用户B的网关会拒收这条消息,因为它的映射中并没有这条规则。
5、但是用户A的网关就会增加了一条允许规则,允许接收从B发送过来的消息
6、服务器要求用户B发送一个消息到用户A的外网IP与端口号
7、用户B发送一条消息,这时用户A就可以接收到B的消息,而且网关B也增加了允许规则
8、之后,由于网关A与网关B都增加了允许规则,所以A与B都可以向对方的外网IP和端口号发送消息。
TCP打洞 技术:
tcp打洞也需要NAT设备支持才行。
tcp的打洞流程和udp的基本一样,但tcp的api决定了tcp打洞的实现过程和udp不一样。
tcp按cs方式工作,一个端口只能用来connect或listen,所以需要使用端口重用,才能利用本地nat的端口映射关系。(设置SO_REUSEADDR,在支持SO_REUSEPORT的系统上,要设置这两个参数。)
连接过程:(以udp打洞的第2种情况为例(典型情况))
nat后的两个peer,A和B,A和B都bind自己listen的端口,向对方发起连接(connect),即使用相同的端口同时连接和等待连接。因为A和B发出连接的顺序有时间差,假设A的syn包到达B的nat时,B的syn包还没有发出,那么B的nat映射还没有建立,会导致A的连接请求失败(连接失败或无法连接,如果nat返回RST或者icmp差错,api上可能表现为被RST;有些nat不返回信息直接丢弃syn包(反而更好)),(应用程序发现失败时,不能关闭socket,closesocket()可能会导致NAT删除端口映射;隔一段时间(1-2s)后未连接还要继续尝试);但后发B的syn包在到达A的nat时,由于A的nat已经建立的映射关系,B的syn包会通过A的nat,被nat转给A的listen端口,从而进去三次握手,完成tcp连接。
从应用程序角度看,连接成功的过程可能有两种不同表现:(以上述假设过程为例)
1、连接建立成功表现为A的connect返回成功。即A端以TCP的同时打开流程完成连接。
2、A端通过listen的端口完成和B的握手,而connect尝试持续失败,应用程序通过accept获取到连接,最终放弃connect(这时可closesocket(conn_fd))。
多数Linux和Windows的协议栈表现为第2种。
但有一个问题是,建立连接的client端,其connect绑定的端口号就是主机listen的端口号,或许这个peer后续还会有更多的这种socket。虽然理论上说,socket是一个五元组,端口号是一个逻辑数字,传输层能够因为五元组的不同而区分开这些socket,但是是否存在实际上的异常,还有待更多观察。
1、Windows XP SP2操作系统之前的主机,这些主机不能正确处理TCP同时开启,或者TCP套接字不支持SO_REUSEADDR的参数。需要让AB有序的发起连接才可能完成。
上述tcp连接过程,仅对NAT1、2、3有效,对NAT4(对称型)无效。
由于对称型nat通常采用规律的外部端口分配方法,对于nat4的打洞,可以采用端口预测的方式进行尝试。
ALG(应用层网关) :它可以是一个设备或插件,用于支持SIP协议,主要类似与在网关上专门开辟一个通道,用于建立内网与外网的连接,也就是说,这是一种定制的网关。更多只适用于使用他们的应用群体内部之间。
UpnP :它是让网关设备在进行工作时寻找一个全球共享的可路由IP来作为通道,这样避免端口造成的影响。要求设备支持且开启upnp功能,但大部分时候,这些功能处于安全考虑,是被关闭的。即时开启,实际应用效果还没经过测试。
STUN(Simple Traversalof UDP Through Network): 这种方式即是类似于我们上面举例中服务器C的处理方式。也是目前普遍采用的方式。但具体实现要比我们描述的复杂许多,光是做网关Nat类型判断就由许多工作,RFC3489中详细描述了。
TURN(Traveral Using Relay NAT): 该方式是将所有的数据交换都经由服务器来完成,这样NAT将没有障碍,但服务器的负载、丢包、延迟性就是很大的问题。目前很多游戏均采用该方式避开NAT的问题。这种方式不叫p2p。
ICE(Interactive Connectivity Establishment): 是对上述各种技术的综合,但明显带来了复杂性。