Internet的迅速发展以及IPv4 地址数量的限制使得网络地址翻译(NAT,Network Address Trans2lation)设备获得普遍应用。NAT设备容许处于同一NAT后的多台主机共享一个公网(本文将处于同一NAT后的网络称为私网,处于NAT前的网络称为公网) IP 地址。一个私网IP 地址经过NAT设备与公网的其余主机通讯。公网和私网IP地址域,以下图所示: 服务器
广域网与私网示意图 网络
通常来讲都是由私网内主机(例如上图中“电脑A-01”)主动发起链接,数据包通过NAT地址转换后送给公网上的服务器(例如上图中的“Server”),链接创建之后可双向传送数据,NAT设备容许私网内主机主动向公网内主机发送数据,但却禁止反方向的主动传递,但在一些特殊的场合须要不一样私网内的主机进行互联(例如P2P软件、网络会议、视频传输等),TCP穿越NAT的问题必须解决。网上关于UDP穿越NAT的文章不少,并且还有配套源代码,可是我我的认为UDP数据虽然速度快,可是没有保障,并且NAT为UDP准备的临时端口号有生命周期的限制,使用起来不够方便,在须要保证传输质量的应用上TCP链接仍是首选(例如:文件传输)。 并发
网上也有很多关于TCP穿越NAT(即TCP打洞)的介绍文章,但不幸我还没找到相关的源代码能够参考,我利用空余时间写了一个能够实现TCP穿越NAT,让不一样的私网内主机创建直接的TCP通讯的源代码。 翻译
这里须要介绍一下NAT的类型: 视频
NAT设备的类型对于TCP穿越NAT,有着十分重要的影响,根据端口映射方式,NAT可分为以下4类,前3种NAT类型可统称为cone类型。 生命周期
(1)全克隆( Full Cone) : NAT把全部来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。任何一个外部主机都可经过该映射发送IP包到该内部主机。 路由
(2)限制性克隆(Restricted Cone) : NAT把全部来自相同内部IP地址和端口的请求映射到相同的外部IP地址和端口。可是,只有当内部主机先给IP地址为X的外部主机发送IP包,该外部主机才能向该内部主机发送IP包。 io
(3)端口限制性克隆( Port Restricted Cone) :端口限制性克隆与限制性克隆相似,只是多了端口号的限制,即只有内部主机先向IP地址为X,端口号为P的外部主机发送1个IP包,该外部主机才可以把源端口号为P的IP包发送给该内部主机。 软件
(4)对称式NAT ( Symmetric NAT) :这种类型的NAT与上述3种类型的不一样,在于当同一内部主机使用相同的端口与不一样地址的外部主机进行通讯时, NAT对该内部主机的映射会有所不一样。对称式NAT不保证全部会话中的私有地址和公开IP之间绑定的一致性。相反,它为每一个新的会话分配一个新的端口号。 路由器
咱们先假设一下:有一个服务器S在公网上有一个IP,两个私网分别由NAT-A和NAT-B链接到公网,NAT-A后面有一台客户端A,NAT-B后面有一台客户端B,如今,咱们须要借助S将A和B创建直接的TCP链接,即由B向A打一个洞,让A能够沿这个洞直接链接到B主机,就好像NAT-B不存在同样。
实现过程以下(请参照源代码):
一、 S启动两个网络侦听,一个叫【主链接】侦听,一个叫【协助打洞】的侦听。
二、 A和B分别与S的【主链接】保持联系。
三、 当A须要和B创建直接的TCP链接时,首先链接S的【协助打洞】端口,并发送协助链接申请。同时在该端口号上启动侦听。注意因为要在相同的网络终端上绑定到不一样的套接字上,因此必须为这些套接字设置 SO_REUSEADDR 属性(即容许重用),不然侦听会失败。
四、 S的【协助打洞】链接收到A的申请后经过【主链接】通知B,并将A通过NAT-A转换后的公网IP地址和端口等信息告诉B。
五、 B收到S的链接通知后首先与S的【协助打洞】端口链接,随便发送一些数据后当即断开,这样作的目的是让S能知道B通过NAT-B转换后的公网IP和端口号。
六、 B尝试与A的通过NAT-A转换后的公网IP地址和端口进行connect,根据不一样的路由器会有不一样的结果,有些路由器在这个操做就能创建链接(例如我用的TPLink R402),大多数路由器对于不请自到的SYN请求包直接丢弃而致使connect失败,但NAT-A会纪录这次链接的源地址和端口号,为接下来真正的链接作好了准备,这就是所谓的打洞,即B向A打了一个洞,下次A就能直接链接到B刚才使用的端口号了。
七、 客户端B打洞的同时在相同的端口上启动侦听。B在一切准备就绪之后经过与S的【主链接】回复消息“我已经准备好”,S在收到之后将B通过NAT-B转换后的公网IP和端口号告诉给A。
八、 A收到S回复的B的公网IP和端口号等信息之后,开始链接到B公网IP和端口号,因为在步骤6中B曾经尝试链接过A的公网IP地址和端口,NAT-A纪录了这次链接的信息,因此当A主动链接B时,NAT-B会认为是合法的SYN数据,并容许经过,从而直接的TCP链接创建起来了。