原文:http://blog.csdn.net/ustcgy/article/details/5655050

5. NAT穿透
5.1 转发     
最可靠但又是最低效的点对点通信方法,莫过于将p2p网络通信看作一个C/S结构,通过服务器来转发信息.如下图,两个客户端A和B,均与服务器S初始化了一个TCP或UDP连接,服务器S具有公网固定IP地址,两个客户端分布在不同的私网中,这样,他们各自的NAT代理服务器将不允许他们进行直连.
                                    Server S
                                          |
                                          |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
            |                                                           |
            |                                                           |
         Client A                                              Client B
         
取而代之的方式是,两个客户端可以把服务器S当作信使来转发消息.比如,为了将消息发送到B,A先发送一条信息给服务器S,服务器S再利用初始化时已经建立的连接,将信息转发给B.

这个方法的优势是:它适合于任何NAT包括Symmetric NAT.但是它的劣势也很明显:它将全面依赖并消耗服务器的资源和网络带宽.名为 TURN 的协议定义了一个利用转发技术进行可靠通信的模型.

5.2 反向连接

这里介绍第二种技术,但是它只能在通信的两端只有一端处于NAT之后的情况下.举例来说,假设客户端A处于NAT之后,而客户端B有一个公网IP地址,如下图所示.        
   
                                   Server S
                            18.181.0.31:1235
                                         |
                                         |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                     |
    155.99.25.11:62000                                    |
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                               138.76.29.7:1234

现在我们假设客户端B将会与客户端A初始化一个端对端连接会话.B将首先试图连接A的一个地址---客户端A认为是它自己的地址10.0.0.1:1234或者是从服务器S观察到的地址155.99.25.11:62000.然而不论是连接哪一个,都不可能成功.第一种情况:试图直接连到10.0.0.1肯定会失败,因为10.0.0.1根本就不是一个可以在公网上路由的IP地址;第二种情况,从B传来的请求将能够到达端口NAT A的端口62000,但NAT A却会拒绝这个连接请求,因为只有外出的连接才允许进入.    在所有的尝试都失败之后,客户端B就只能通过服务器S来请求A做一个"反向"连接到客户端B,客户端A将打开一个与客户端B通讯的连接(在B的公网IP地址和端口号上).NAT A允许这个连接通过,因为这个连接起源于NAT A的内部,并且同时客户端B能够受这个连接因为B并不位于NAT之后.

这个方法的优势是:它也适合于任何NAT包括Symmetric NAT.它的主要限制在于,只能有一端位于NAT之后.

5.3 UDP打洞
第三种技术,也是这篇文章主要要介绍的,就是非常有名的"UDP打洞技术".这里将考虑两种典型场景,来介绍连接的双方应用程序如何按照计划的进行通信的,第一种场景,我们假设两个客户端都处于不同的NAT之后;第二种场景,我们假设两个客户端处于同一个NAT之后,但是它们彼此都不知道(他们在同一个NAT中).

5.3.1 处于不同NAT之后的客户端通信
我们假设 Client A和Client B都拥有自己的私有IP地址,并且都处在不同的NAT之后,端对端的程序运行于 CLIENT A,CLIENT B,S之间,并且它们都开放了UDP端口1234. CLIENT A和CLIENT B首先分别与S建立通信会话,这时NAT A把它自己的UDP端口62000分配给CLIENT A与S的会话,NAT B也把自己的UDP端口31000分配给CLIENT B与S的会话.如下图所示:
                                Server S
                            18.181.0.31:1234
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    155.99.25.11:62000                            138.76.29.7:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234

假如这个时候 CLIENT A 想与 CLIENT B建立一条UDP通信直连,如果 CLIENT A只是简单的发送一个UDP信息到CLIENT B的公网地址
138.76.29.7:31000的话,NAT B会不加考虑的将这个信息丢弃(除非NAT B是一个 full cone NAT),因为这个UDP信息中所包含的地址信息,与CLIENT B和服务器S建立连接时存储在NAT B中的服务器S的地址信息不符.同样的,CLIENT B如果做同样的事情,发送的UDP信息也会被NAT A丢弃.

假如 CLIENT A 开始发送一个UDP信息到CLIENT B的公网地址上,与此同时,他又通过S中转发送了一个邀请信息给CLIENT B,请求CLIENT B也给CLIENT A发送一个UDP信息到 CLIENT A的公网地址上.这时CLIENT A向CLIENT B的公网IP(138.76.29.7:31000)发送的信息导致 NAT A 打开一个处于CLIENT A的私有地址和CLIENT B的公网地址之间的新的通信会话,与此同时NAT B也打开了一个处于CLIENT B的私有地址和CLIENT A的公网地址(155.99.25.11:62000)之间的新的通信会话.一旦这个新的UDP会话各自向对方打开了,CLIENT A和CLIENT B之间就可以直接通信,而无需S来牵线搭桥了.这就是所谓的打洞技术.
一旦这种处于NAT之后的端对端的直连建立之后,连接的双方可以轮流担任对方的"媒人",把对方介绍给其他的客户端,这样就极大的降低了服务器S的工作量.

5.3.2 处于相同NAT之后的客户端通信
我们假设 Client A和Client B都拥有自己的私有IP地址,并且都处在相同的NAT之后,端对端的程序运行于 CLIENT A,CLIENT B,S之间,
CLIENT A和CLIENT B分别与S建立通信会话,经过NAT转换后,A的公网端口被映射为62000,B的公网端口映射为62001.如下图所示:

Server S
                            18.181.0.31:1234
                                   |
                                   |
                                  NAT
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
            +----------------------+----------------------+
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
      
根据前面介绍的"打洞"技术,CLIENT A将发送一个UDP信息到CLIENT B的公网地址上,数据包源端为(10.0.0.1:124),目的端为(155.99.25.11:62001).该数据包能否被B收到,取决于当前的NAT是否支持"发夹"转换(hairpin转换,也就是同一台设备不同端口之间的UDP数据包能否到达).
首先,支持"发夹"转换的NAT设备还远没有支持"打洞"技术的NAT设备多,其次,即使NAT设备支持"发夹"转换,在这种情况下也应该通过网内端到端实现,而不是将数据包无谓 地经过NAT设备,这是一种对资源的浪费.

5.3.3 一般"打洞"过程
综合上面介绍的客户端处于不同NAT之后和处于同一NAT之后,我们说下一般的"打洞"过程.
1. 打洞技术假定客户端A和B可以与公网内的已知的集中服务器建立UDP连接(可以互发UDP数据包).当一个客户端在S上登陆的时候,服务器记录下该客户端的两个endpoints(IP地址,UDP端口),一个是该客户端确信自己是通过该ip和端口与服务器S进行通信的,另一个是服务器S记录下的由服务器"观察"到的该客户端实际与自己通信所使用的ip和端口.我们可以把前一个endpoint看作是客户端的内网ip和端口,把后一个endpoint看作是客户端的内网ip和端口经过NAT转换后的公网ip和端口.服务器可以从客户端的登陆消息的消息体中得到该客户端的内网endpoint相关信息,可以通过对登陆消息的IP或UDP头得到该客户端的公网endpoint.
2.假设Client A想向B发起连接,于是A向服务器S发送消息,请求S帮助建立与B的UDP连接.这时,S将B的公网和内网的endpoint发给A.可知,A与B通过与S的一次通信就可以知道对方的公网和内网的endpoint.     
3.Client A通过B的内网endpoint发送UDP数据包.针对5.3.2节问题的解决方案.如果B和A在同一NAT后,则很快收到响应.如果B和A不在同一NAT后,则超时.
4.Client A通过B的外网endpoint发送UDP数据包.
回到5.3.1节介绍的具体方法.CLIENT A发出UDP包(10.0.0.1:1234,138.76.29.7:31000),经NAT A转换为(155.99.25.11:62000,138.76.29.7:31000),经NAT B转换为(155.99.25.11:62000,10.1.1.3:1234).如果在此数据包到达NAT B前,B发送过UDP包到A的公网endpoint,则NAT B允许此包到达B机.在5.3.2节下,A发出UDP包(10.0.0.1:1234,155.99.25.11:62001),NAT 先转换为(155.99.25.11:62000,155.99.25.11:62001),再转换为(155.99.25.11:62000,10.1.1.3:1234).
5.步骤3,4发送的数据包是为了"打洞",打洞成功后,就进入真正的P2P传输了.
还有一种情况,B和A不在同一NAT后,C和A在同一NAT后,且B和C的内网endpoint一致,这个时候A从S拿到的目的端应该有2个.所以针对3,4步骤取先有回应的目的端不可取,应该先做步骤3,有回应直接到步骤5,没有回应到步骤4.

5.3.4 客户端分别处于多层NAT之后
在有些网络拓扑中就存在多层NAT设备,让我们来看看下图这种情况:
假如 NAT X 是由 Internet服务供应商(ISP)配置的一个大型NAT,它使用少量的公网IP地址来为一些客户群提供服务,NAT A和NAT B则是
为ISP的两个客户群所配置的小一点的独立NAT网关,它们为各自客户群的私人家庭网络提供IP地址.只有Server S和NAT X拥有公网固定IP地址,而NAT A 和 NAT B所拥有的"公网"IP地址对于ISP的寻址域来说则实际上"私有"的.

Server S
                            18.181.0.31:1234
                                   |
                                   |
                                 NAT X
                         A-S 155.99.25.11:62000
                         B-S 155.99.25.11:62001
                                   |
                                   |
            +----------------------+----------------------+
            |                                                           |
          NAT A                                                 NAT B
    192.168.1.1:30000                             192.168.1.2:31000
            |                                                           |
            |                                                           |
         Client A                                              Client B
      10.0.0.1:1234                                 10.1.1.3:1234
现在让我们假设Client A和Client B想要建立一条端对端 的UDP直连.Client A和 Client B只知道Server S记录的他们真正的公网地址
155.99.25.11:62000和155.99.25.11:62001,而且他们只能通过这个公网地址建立连接,即NAT X必须得支持"loopback translation"(也称hairpin转换)才行.

Client A和 Client B也知道对方内网地址10.0.0.1:1234和10.1.1.3:1234,毫无疑问,通过内网地址是建立不了连接的.Client A和 Client B并不知道对方NAT B和NAT A的地址192.168.1.2:31000和192.168.1.1:30000,即便假设我们通过某种途径得知了这些地址,还是不能够保证这样就能进行通话了,因为这些地址是由ISP的私有寻址域分配的,可能会与私有域所分配的其他无关客户端地址相冲突.

5.3.5 UDP在空闲状态下的超时问题
由于UDP转换协议提供的"洞"不是绝对可靠的,多数NAT设备内部都有一个UDP转换的空闲状态计时器,如果在一段时间内没有UDP数据通信,NAT设备会关掉由"打洞"操作打出来的"洞",做为应用程序来讲如果想要做到与设备无关,就最好在穿越NAT的以后设定一个穿越的有效期.很遗憾目前没有标准有效期,这个有效期与NAT设备内部的配置有关,最短的只有20秒左右.在这个有效期内,即使没有p2p数据包需要传输,应用程序为了维持该"洞"可以正常工作,也必须向对方发送"打洞"维持包.这个维持包是需要双方应用都发送的,只有一方发送不会维持另一方的session正常工作.除了频繁发送"打洞"维持包以外,还有一个方法就是在当前的"洞"有效期过期之前,p2p客户端双方重新"打洞",丢弃原有的"洞",这也不失为一个有效的方法.

5.4 . UPD端口号预言

在使用"UDP打洞技术"时有一点必须要注意:它只能在双方的NAT都是cone NAT时才能正常工作.这些NAT在使用时保持着端口的
绑定----[私有IP,私有UDP端口]对和[公网IP,公网UDP端口]对的一一对应.
如果像 symmetricNAT那样给每个新的会话都分配一个新的公网端口,那么UDP应用程序想要与其他外部客户端进行通话,就无法重复使用已经建立好的通信转换.

让我们来考虑这样一种情况,有两个客户端A和B,他们都藏在不同的Symmetric NAT后面,他们都开放了一个UDP连接给具有固定IP的Server S.如下图:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
       Symmetric NAT A                               Symmetric NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
              |                                                           |
              |                                                           |
           Client A                                             Client B
        10.0.0.1:1234                                 10.1.1.3:1234
        
NAT A 分配了它自己的UDP端口62000,用来保持客户端A与服务器S的通信会话,NAT B 也分配了31000端口,用来保持客户端B与 
服务器S的通信会话.通过与服务器S的对话,客户端A和客户端B都相互知道了对方所映射的真实IP和端口.
客户端A发送一条UDP消息到 138.76.29.7:31001(请注意到端口号的增加),同时客户端B发送一条UDP消息到 155.99.25.11:62001.
如果NAT A 和NAT B继续分配端口给新的会话,并且从A-S和B-S的会话时间消耗得并不多的话,那么一条处于客户端A和客户端B之间的双向会话通道就建立了. 
客户端A发出的消息送达B导致了NAT A打开了一个新的会话,并且我们希望 NAT A将会指派62001端口给这个新的会话,因为62001是
继62000后,NAT会自动指派给 从服务器S到客户端A之间的新会话的端口号;类似的,客户端B发出的消息送达A导致了NAT B打开了
一个新的会话,并且我们希望 NAT B将会指派31001这个端口给新的会话;如果两个客户端都正确的猜测到了对方新会话被指派的端口号,
那么这个客户端A----客户端B的双向连接就被打通了.其结果如下图所示:
                                  Server S
                              18.181.0.31:1234
                                     |
                                     |
              +----------------------+----------------------+
              |                                                           |
            NAT A                                                  NAT B
   A-S 155.99.25.11:62000                        B-S 138.76.29.7:31000
   A-B 155.99.25.11:62001                        B-A 138.76.29.7:31001
              |                                                           |
              |                                                           |
           Client A                                              Client B
        10.0.0.1:1234                                 10.1.1.3:1234
明显的,有许多因素会导致这个方法失败:如果这个预言的新端口(62001和31001) 恰好已经被一个不相关的会话所使用,那么NAT就会跳过这个端口号,这个连接就会宣告失败;如果两个NAT有时或者总是不按照顺序来生成新的端口号,那么这个方法也是行不通的.如果隐藏在NAT A后的一个不同的客户端X(或者在NAT B后)打开了一个新的"外出"UDP 连接,并且无论这个连接的目的如何,只要这个动作发生在客户端A建立了与服务器S的连接之后,客户端A与客户端B建立连接之前,那么这个无关的客户端X 就会趁人不备地"偷"到这个我们渴望分配的端口.所以,这个方法变得如此脆弱而且不堪一击,只要任何一个NAT方包含以上碰到的问题,这个方法都不会奏效. 
 最后,如果P2P的一方处在两级或者两级以上的NAT下面,并且这些NAT接近这个客户端是symmetric的话,端口号预言是无效的.
因此,并不推荐使用这个方法来写新的P2P应用程序.

5.5 同时开放TCP连接

这里有一种方法能够在某种情况下建立一个穿透NAT的端对端TCP直连.我们知道,绝大多数的TCP会话的建立,都是通过一端先发送一个SYN包开始,另一方则回发一个SYN-ACK包的过程.然而,这里确实存在另外一种情况,就是P2P的双方各自同时地发出一个SYN包到对方的公网地址上,然后各自都单独地返回一个ACK响应来建立一个TCP会话.这个过程被称之为"Simultaneous open"(同时开放连接).

如果一个NAT接收到一个来自私有网络外面的TCP SYN包,这个包想发起一个"引入"的TCP连接,一般来说,NAT会拒绝这个连接请求并扔掉这个SYN 包,或者回送一个TCP RST(connection reset,重建连接)包给请求方.但是,有一种情况,当这个接收到的SYN包中的源IP地址和端口,目标IP地址和端口都与NAT登记的一个已经激活的TCP会话中的地址信息相符时,NAT将会放行这个SYN 包,让它进入NAT内部.特别要指出,如果NAT恰好看到一个刚刚发送出去的一个SYN包也和上面接收到的SYN包中的地址信息相符合的话,那么NAT将会认为这个TCP连接已经被激活,并将允许这个方向的SYN包进入NAT内部.

如果Client A和Client B能够彼此正确的预知对方的NAT将会给下一个TCP连接分配的公网TCP端口,并且两个客户端能够同时地发起一个"外出"的TCP连接,并在对方的SYN包到达之前,自己刚发送出去的SYN包都能顺利的穿过自己的NAT的话,一条端对端的TCP连接就成功地建立了.
不幸的是,这个诡计比5.4节所讲的UDP端口预言更容易被粉碎,并且对时间的敏感性的依赖更多.

这个属于TCP穿透,它同样基于NAT对TCP穿透的支持,基本原理和UDP穿透一致.具体实现后面会继续介绍.

转载于:https://www.cnblogs.com/yinsua/p/4567047.html

(转)NAT与NAT穿透 原理相关推荐

  1. P2P通信原理与实现(C++),NAT,网络穿透原理

    1.简介 当今互联网到处存在着一些中间件(MIddleBoxes),如NAT和防火墙,导致两个(不在同一内网)中的客户端无法直接通信.这些问题即便是到了IPV6时代也会存在,因为即使不需要NAT,但还 ...

  2. 通俗易懂:快速理解ipv4的NAT穿透原理

    NAT基础   IPv4由于最初的设计原因,长度只有32位,所以只提供了大约40亿个地址.这造成了地址耗尽危机.   NAT(Network Address Translation,网络地址转换),也 ...

  3. P2P之NAT穿透原理

    P2P之NAT穿透原理介绍 背景 一次项目中,对于主动协议接入的设备,客户希望能够设备端直接推送码流到客户端以此减少中心媒体的负载.所以对P2P这块方案做了了解,这里做下整理. 基本概念 P2P对等网 ...

  4. P2P如何助力音视频传输,彻底熟悉P2P丨NAT的作用丨网络穿透原理到实战

    P2P如何助力音视频传输,一次课通透P2P 1. NAT的作用 2. 网络穿透的原理 3. 网络穿透实战 [技术分享篇]P2P如何助力音视频传输,彻底熟悉P2P丨NAT的作用丨网络穿透原理到实战 内容 ...

  5. p2p之网络穿透NAT,NAT、穿透的原理

    1.p2p是什么? p2p是对等网络(peer-to-peer networking)其可以定义为:端对端的资源共享,每一端即可是服务端,也可以是客户端.既可以是资源的提供者,也可以是资源的共享者. ...

  6. P2P技术详解(一):NAT详解——详细原理、P2P简介(转)

    这是一篇介绍NAT技术要点的精华文章,来自华3通信官方资料库,文中对NAT技术原理的介绍很全面也很权威,对网络应用的应用层开发人员而言有很高的参考价值. <P2P技术详解>系列文章 ➊ 本 ...

  7. 015. P2P技术详解(一):NAT详解——详细原理、P2P简介

    http://www.52im.net/thread-50-1-1.html 这是一篇介绍NAT技术要点的精华文章,来自华3通信官方资料库,文中对NAT技术原理的介绍很全面也很权威,对网络应用的应用层 ...

  8. pwnat——一种无需第三方服务器就能完成NAT点对点P2P穿透的基于UDP打洞技术的新方法

    pwnat--一种无需第三方服务器就能完成NAT点对点P2P穿透的基于UDP打洞技术的新方法 简介 传统的udp打洞 UDP 打洞原理及过程 pwnat技术,无需第三方服务器! pwnat用法 pwn ...

  9. C# dotnet core TCP NAT UDP P2P 穿透 小引

    Mark:Linyee Jiaguoxinzhi TCP NAT UDP P2P 穿透 国内 p2p 文章偏少了. 其实不会,,只是加上C#就比较少了.其实还是很多,只是有点千篇一律. dotnet ...

最新文章

  1. 获取局域网打印机列表
  2. 学完javase和mysql_Java基础学完接下来应该学什么呢?
  3. html调用百度地图语音播报,实现百度地图导航演示的语音播放功能
  4. ***“出更”---获取源码的***
  5. 云服务器BBC销售渠道,云服务器BBC控制台
  6. mysql varchar最多可以存多少汉字_MySQL定义char和varchar类型utf8编码最大值
  7. 社交电商为什么这么火
  8. position有哪些属性?
  9. Mac系统 python3.7安装
  10. mysql服务启动成功后卸载_安装,启动与卸载Mysql系统服务(MYSQL常见问题)
  11. 在学习C语言和C++初期的疑惑有哪些?
  12. Android入门笔记02
  13. Android Q Default Ringtone 客制化SKUID默认来电铃声/通知铃声配置
  14. 极客日报第 35 期:国外运营商拒为小米 10T Pro 启用双卡;苹果明年 9 月或发布电动汽车;谷歌被指与 Facebook 密谋垄断
  15. 免费stm32视频教程分享:心率检测仪的设计与实现
  16. 一个高中生的编程自学经历
  17. iPhone录音转文字怎么操作?手把手教你详细流程
  18. 兵法三十六计第二计-围魏救赵。
  19. 【Spring源码】Spring事务原理
  20. matlab 年积日与年月日转换,空间大地测量与GPS导航定位时间系统相互转换,格里高利时通用时儒略日,GPS时,年积日相互转换的源代码程序...

热门文章

  1. 堆的C语言实现——堆与堆排序(二)
  2. RTSP再学习 -- 利用FFmpeg 将 rtsp 获取H264裸流并保存到文件中
  3. 2017年Q1安卓ROOT类恶意病毒发展趋势研究报告
  4. mysql my.cnf参数配置_MySQLmy.cnf参数配置优化详解
  5. JZOJ 5628. 【NOI2018模拟4.4】Travel
  6. JZOJ 100035. 【NOIP2017提高A组模拟7.10】区间
  7. docker yum php mysql_Centos下 使用Docker, 配置PHP+Nginx+Mysql(多PHP版本)
  8. java调用python项目实战_Java调用Python
  9. python import出错_Python ImportError: cannot import name urlopen错误分析
  10. windows聚焦图片为什么不更新了_为什么头条粉丝数到几百,就涨不起来了,一直在辛苦更新...