tutk-p2p /NAT / p2p 如何打洞

TUTK p2p穿透的使用介绍:
https://blog.csdn.net/jakejohn/article/details/87445555
打孔,nat 理论概念的解读
http://itindex.net/detail/59082-tutk-p2p-%E5%8E%9F%E7%90%86

补充知识&补充知识&补充知识&补充知识&补充知识&补充知识&补充知识&补充知识&补充知识&

*1. NAT的作用是什么?

NAT的作用是把内网的私有地址,转化成外网的公有地址。使得内部网络上的(被设置为私有IP地址的)主机可以访问Internet。

2. NAT分为哪几种?*
NAT可以分为Basic NAT和PAT:
Basic NAT只转化IP,不映射端口。
PAT除了转化IP,还做端口映射,可以用于多个内部地址映射到少量(甚至一个)外部地址。
NAT还可以分为静态NAT和动态NAT:
静态NAT,将内部网络中的每个主机都永久映射成外部网络中的某个合法的地址,多用于服务器。
动态NAT,则是在外部网络中定义了一个或多个合法地址,采用动态分配的方法映射到内部网络。
3为什么需要有NAT?
NAT的主要作用,是解决IP地址数量紧缺。当大量的内部主机只能使用少量的合法的外部地址,就可以使用NAT把内部地址转化成外部地址。
NAT还可以防止外部主机攻击内部主机(或服务器)。
4. 怎样映射?
如何将大量的内部地址,映射成少量的外部地址?
对于第四层是TCP或UDP的数据包,NAT通过更改源端口号,来实现多对少的映射。
例如:内部IP1~IP4,4个地址映射成外部一个地址IP5。
(IP1,Port1)映射成(IP5,Port1)
(IP2,Port1)映射成(IP5,Port2)
(IP3,Port2)映射成(IP5,Port3)
(IP4,Port2)映射成(IP5,Port4)
对于ICMP包,NAT通过更改ICMP的ID,来实现多对少的映射。
5.TCP或UDP的端口,原本是用来做什么的?
端口号是用来连接上层程序的。例如,端口号23,对应了Telnet;端口号80,对应了Http等等。
因此,在本动画中,当R1转化H1发送给Server的TCP包的时候,不能转化目的地端口。Server正是通过端口号23,才知道把收到的TCP交给Telnet处理。

以下转自:https://blog.csdn.net/qq_30145355/article/details/78992731

一、P2P打洞原理
#####1、打洞解决了什么问题?#####
我们平常使用的一般都为私有ip,但是私有ip之间是不能直接通信的,如果要进行通信只能通过公网上的服务器进行数据的转发,难道我们每次发送数据都要经过公网上的服务器转发吗?也不是不可以,但是服务器的承受能力就会大大增加。此时就需要我们的打洞技术的出现了,打洞的出现解决了私有ip之间直接通信的问题(还是需要经过一次公网服务器)
例如:QQ中的聊天就广泛的使用到了打洞技术,不然服务器的承受能力会大大增加,而且会影响聊天的效率。

#####2、打洞的实现过程与原理#####
私有ip的数据都要经过路由器的转发,路由器上有一张NAPT表(IP端口映射表),NAPT表记录的是【私有IP:端口】与【公有IP:端口】的映射关系(就是一一对应关系),本文讲到的路由均是以NAPT为工作模式,这并不影响对打洞。实际中的数据实际发送给的都是路由器的【公有IP:端口】,然后经过路由器进过查询路由表后再转发给【私有的IP:端口】的。

举个示例:
用户A
电脑IP:192.168.1.101
桌面上有个客户端程序采用的网络端口:10000
路由器的公有IP:120.78.201.201(实际中常常为多级路由,这里以最简单的一层路由举例)
NAPT路由器的NAPT表的其中一条记录为:【120.78.201.201:20202】-【192.168.1.101:10000】

用户B
电脑IP:192.168.2.202
桌面上有个客户端程序采用的网络端口:22222
路由器的公有IP:120.78.202.202
NAPT路由器的NAPT表的其中一条记录为:【120.78.202.202:20000】-【192.168.2.202:22222】

打洞服务器P2Pserver
IP:120.78.202.100
port:20000

此时用户A的电脑发给了服务器一条数据,服务器收到用户A的IP与端口是多少呢?当然为120.78.201.201:20202,数据包经过路由的时候进行了重新的封包。如果服务器此时发一条数据给用户A,发往的IP与端口是什么呢?当然为120.78.201.201:20202,此时路由器收到这个数据包后,进行查询NAPT表中120.78.201.201:20202对应的IP与端口信息,发现是192.168.1.101:10000,然后路由器就转发给IP为192.168.1.101:10000的电脑,然后电脑上的应用程序就收到这条信息了。

既然如此,我们私有IP虽然不能直接通信,但是我们能够发给公有IP!如果用户B需要给用户A发一条信息时,用户B直接将数据发往目的IP、端口为120.78.201.201:20202的地方不就行了?
这里有两个问题:
第一,用户B怎么知道用户A在路由上映射的IP与端口;
第二,用户B直接将数据包发往120.78.201.201:20202,路由器是会将用户B的数据包丢弃的,因为路由器里面没有关于用户B120.78.202.202的路由信息(路由器里面还有个路由表,用于路由),无法进行路由,所以将会进行丢弃。

如何解决第一个问题?
通过打洞服务器,将用户A映射的IP、端口信息告诉用户B即可。
如何解决第二个问题?
如果打洞服务器首先告诉用户A先发一条信息给用户B(用户A得知用户B的地址信息也是通过打洞服务器),注意此时用户B是收不到的,用户B的路由同样会进行丢弃,但是这并不要紧,因为用户A发了这条信息后,用户A的路由就会记录关于用户B的路由信息(该信息记录的是将用户B的IP信息路由到用户A电脑),然后此时用户B再发给用户A一条信息,就不会进行丢弃了,因为用户A的路由里面有用户B的路由信息。

通过解决上面的两个问题后,我们再通过图形示意图来具体了解下打洞的过程
在这里插入图片描述
整个过程就是我标的序号1->2->3->4->5->6->7->8>9
过程一:1->2
此过程为用户B向服务器请求向用户A打洞
过程二:3->4
此过程为服务器相应用户B的打洞请求,告诉用户A用户B想与你打洞(数据包中包含用户B的地址信息)。
过程三:5->6
用户A主动发一条信息给用户B,目的是为了使得路由器A中能够有一条关于路由B的IP的路由信息(注意不是用户B,用户B是私有IP),就如图所示,这条信息会被丢弃的,因为路由B的路由表中没有路由A的IP的信息。
过程四:7->8->9
用户B再发一条信息给用户A,因为此时路由A的路由表中有关于路由B的IP的路由信息,此时路由A就能路由给用户A了,至此,用户A就能直接收到用户B发的信息了。注意,此时用户A发给用户B不需要打洞,因为路由B中已经有关于路由A的IP的路由信息了。

&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&&
以下内容转自:
https://blog.csdn.net/u014023993/article/details/86563976
概述

P2P简介
我们知道,内网设备是不能直接访问公网的,如果需要内网设备可以访问公网,需要借助NAT(Net Address Transmit)。如果告诉你,可以不通过公网,可以实现两个不同网络的内网设备间的直接通信,你信不信?通过最基本的认知分析,上述说法好像有问题:不通过公网,怎么可能实现两个内网设备的交互呢?如果有这种方法,那么肯定会在某个方面用到公网。

P2P就是这样的技术,可以使得不同局域网的内网设备可以直连。P2P(peer to peer)点对点技术又称对等互联网络技术,是一种网络新技术,依赖网络中参与者的计算能力和带宽,而不是把依赖都聚集在较少的几台服务器上。P2P网络通常用于通过Ad Hoc连接来连接节点。这类网络可以用于多种用途,各种档案分享软件已经得到了广泛的使用(譬如迅雷、电驴)。P2P技术也被使用在类似VoIP等实时媒体业务的数据通信中。
P2P有个很重要的能力,内网穿透能力,具有这个能力后,不同私网的设备可以直接进行通信。

P2P通信技术
根据客户端的不同,客户端之间进行P2P传输的方法也略有不同,P2P主要有中继、逆向连接、打洞(hole punching)等技术。

P2P打洞技术依赖于通常防火墙和锥形NAT(cone NAT)允许正当的P2P应用程序在中间件中打洞且与对方建立直接连接的特性。
根据传输方式的不同,P2P打洞技术分为TCP打洞及UDP打洞技术。

中继(Relaying
中继最可靠但也是最低效的一种P2P通信实现,其原理是通过一个有公网IP的服务器中间人对两个内网客户端的通信数据进行中继和转发,如图 1所示:

在这里插入图片描述
图 1 中继

当服务端连接的客户端比较少,且网络流量不大时,效果还不错。但是如果有很多客户端连接并且网络流量很多,服务端的压力就会很大。

逆向连接(Connection reversal)

此种方法在两个端点中有一个不存在中间件(如NAT)的时候有效。例如,Client A在NAT之后而Client B拥有全局IP地址,如图 2所示:

在这里插入图片描述
图 2 逆向连接

Client A内网地址为10.0.0.1,使用TCP,端口为1234。A和Server S建立了一个连接,Server的IP地址为18.181.0.31,监听1235端口。NAT A给Client A分配了TCP端口62000,地址为NAT的公网IP地址155.99.25.11,作为Client A对外当前会话的临时IP和端口。因此Server S认为Client A就是155.99.25.11:62000。而Client B由于有公网地址,所以对Server S来说Client B就是138.76.29.7:1234。

当Client B想要主动发起对Client A的P2P连接时,需要指定目的地址及端口为155.99.25.11:62000。由于NAT工作的原理问题,NAT A会拒绝将收到的对Client A的请求转发给Client A。拒绝该请求主要有如下原因:

  1. NAT A没有映射过62000端口,NAT A不知道该请求是给谁的

  2. NAT A映射过62000端口,但是需要首先从Client A发起请求,然后才能转发应答

在直接连接Client A失败之后,Client B可以通过Server S向Client A中继一个连接请求,从而从Client A方向“逆向“地建立起Client A- Client B之间的点对点连接(因为Client A连接到了Server S)。

很多当前的P2P系统都实现了这种技术,但其局限性也是很明显的,只有当其中一方有公网IP时连接才能建立。越来越多的情况下,通信的双方都在NAT之后,因此就要用到打洞技术了。

UDP打洞
对于UDP打洞,考虑如下的两张场景:

  1. 两个Client 处在两个不同的NAT

  2. 两个Client 在同一个NAT

端点处于不同NAT
Client A和Client B的地址都是内网地址,且在不同的NAT后面。Client A、Client B上运行的P2P应用程序和Server S都使用了UDP端口1234,Client A和Client B分别初始化了与Server S的UDP通信,地址映射如图 3所示:
在这里插入图片描述
图 3端点在不同的NAT之下
假设Client A打算与Client B直接建立一个UDP通信会话。如果Client A直接给Client B的公网地址138.76.29.7:31000发送UDP数据,NAT B很可能会无视进入的数据(除非是Full Cone NAT),Client B往Client A直接发信息也类似。

为了解决上述问题,在Client A开始给Client B的公网地址发送UDP数据的同时,Client A给Server S发送一个中继请求,要求Client B开始给Client A的公网地址发送UDP信息。Client A往Client B的输出信息会导致NAT A打开一个Client A的内网地址与Client B的外网地址之间的通讯会话,Client B往Client A亦然。当两个方向都打开会话之后,Client A和Client B就能直接通讯,而无须再通过Server S了。

UDP打洞技术有许多有用的性质。一旦一个的P2P连接建立,连接的双方都能反过来作为“引导服务器”来帮助其他中间件后的客户端进行打洞,极大减少了服务器的负载。应用程序不需要知道中间件是什么(如果有的话),因为以上的过程在没有中间件或者有多个中间件的情况下也一样能建立通信链路。

端点处于相同的NAT
如果Client A和Client B正好在同一个NAT(而且可能他们自己并不知道),Client A和Server S建立了一个UDP会话,NAT为此分配了公网端口62000,Client B同样和Server S建立会话,分配到了端口62001,如图 4所示:

图 4端点在相同的NAT之下
假设Client A和Client B使用了上节介绍的UDP打洞技术来建立P2P通路,那么交互流程就是这样了:

  1. Client A和Client B得到由Server S观测到的对方的公网IP和端口号,然后给对方的地址发送信息

  2. 当Client A发送一个UDP数据包给Client B的公网地址,数据包最初有源IP地址和端口地址10.0.0.1:1234和目的地址155.99.25.11:62001

  3. NAT收到包后,将其转换为源155.99.25.11:62000(Client A的公网地址)和目的10.1.1.3:1234,NAT发现目的公网地址和其本身的公网地址相同,此时有两种做法:

  4. 如果NAT支持回环传输(NAT允许同一个内网的主机间相互通信),那么直接将数据发给Client B

  5. 如果NAT不支持回环传输,有可能将该数据丢弃

因为从内部到达NAT的数据会被“回送”到内网中而不是转发到外网。即便NAT支持回环传输,这种转换和转发在此情况下也是没必要的,且有可能会增加A与B的对话延时和加重NAT的负担。

解决上述问题也很简单:

  1. 当Client A和Client B最初通过Server S交换地址信息时,它们包含自身的IP地址和端口号(从自己看),同时也包含从服务器看的自己的地址和端口号

  2. Client 向对方的公网地址及内网地址同时发生数据,并使用第一个成功通信的地址作为对方地址。如果两个Client 在同一个NAT后,发送到对方内网地址的数据最有可能先到达,从而可以建立一条不经过NAT的通信链路

  3. 如果两个Client 在不同的NAT之后,发送给对方内网地址的数据包根本就到达不了对方,但仍然可以通过公网地址来建立通路。

值得一提的是,虽然这些数据包通过某种方式验证,但是在不同NAT的情况下完全有可能会导致Client A往Client B发送的信息发送到其他Client A内网网段中无关的结点上去的。

固定端口绑定
UDP打洞技术有一个主要的条件:只有当两个NAT都是Cone NAT(或者非NAT的防火墙)时才能工作。因为其维持了一个给定的(内网IP,内网UDP)二元组和(公网IP, 公网UDP)二元组固定的端口绑定,只要该UDP端口还在使用中,就不会变化。如果像对称NAT一样,给每个新会话分配一个新的公网端口,就会导致UDP应用程序无法使用跟外部端点已经打通了的通信链路。由于Cone NAT是当今最广泛使用的,尽管有一小部分的对称NAT是不支持打洞的,UDP打洞技术也还是被广泛采纳应用。

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

TCP打洞
建立穿越NAT设备的P2P的TCP连接只比UDP复杂一点点,TCP协议的 “打洞”从协议层来看是与UDP的“打洞”过程非常相似的。尽管如此,基于TCP协议的打洞至今为止还没有被很好的理解,这也造成了的对其提供支持的NAT设备不是很多。在NAT设备支持的前提下,基于TCP的“打洞”技术实际上与基于UDP的“打洞”技术一样快捷、可靠。实际上,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。

套接字和TCP端口的重用
实现基于TCP协议的P2P打洞过程中,最主要的问题不是来自于TCP协议,而是来自于应用程序的API接口。这是由于标准的伯克利(Berkeley)套接字的API是围绕着构建Client /服务器程序而设计的,API允许TCP套接字通过调用connect()函数来建立向外的连接,或者通过listen()和accept函数接受来自外部的连接,但是,API不提供类似UDP那样的,同一个端口既可以向外连接,又能够接受来自外部的连接。而且更糟的是,TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。

为了让TCP打洞能够顺利工作,我们需要使用一个本地的TCP端口来监听来自外部的TCP连接,同时建立多个向外的TCP连接(通过SO_REUSEADDR、SO_REUSEADDR)。

打开P2P的TCP流
假定Client A希望建立与Client B的TCP连接(Client A和Client B已经与公网上的服务器建立了TCP连接),步骤为:

  1. Client A使用其与服务器的连接向服务器发送请求,要求服务器协助其连接Client B;

  2. 服务器将Client B的公网和内网的TCP地址的二元组信息返回给A,同时,服务器将Client A的公网和内网的地址二元组也发送给Client B;

  3. Client A和Client B使用连接服务器的端口异步地发起向对方的公网、内网地址二元组的TCP连接,同时监听各自的本地TCP端口是否有外部的连接联入;

  4. Client A和Client B开始等待向外的连接是否成功,检查是否有新连接联入。如果向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,Client 只需要延迟一段时间(例如延迟一秒钟),然后重新发起连接即可,延迟的时间和重复连接的次数可以由应用程序编写者来确定;

  5. TCP连接建立起来以后,Client 之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。如果鉴权失败,Client 将关闭连接,并且继续等待新的连接联入。Client 通常采用“先入为主”的策略,只接受第一个通过鉴权操作的Client ,然后将进入P2P通信过程不再继续等待是否有新的连接联入。
    图 5 TCP打洞流程图

与UDP不同的是,因为使用UDP协议的每个Client 只需要一个套接字即可完成与服务器的通信,而TCP Client 必须处理多个套接字绑定到同一个本地TCP端口的问题。现在来看实际中常见的一种情景,A与B分别位于不同的NAT设备后面,如图 3所示,并且假定图中的端口号是TCP协议的端口号,而不是UDP的端口号。图中向外的连接代表Client A和Client B向对方的内网地址二元组发起的连接,这些连接或许会失败或者无法连接到对方。如同使用UDP协议进行“打洞”操作遇到的问题一样,TCP的“打洞”操作也会遇到内网的IP与“伪”公网IP重复造成连接失败或者错误连接之类的问题。

Client 向彼此公网地址二元组发起连接的操作,会使得各自的NAT设备打开新的“洞”允许Client A与Client B的TCP数据通过。如果NAT设备支持TCP“打洞”操作的话,一个在Client 之间的基于TCP协议的流通道就会自动建立起来。如果Client A向Client B发送的第一个SYN包发到了Client B的NAT设备,而Client B在此前没有向Client A发送SYN包,Client B的NAT设备会丢弃这个包,这会引起A的“连接失败”或“无法连接”问题。而此时,由于Client A已经向Client B发送过SYN包,Client B发往Client A的SYN包将被看作是由Client A发往Client B的包的回应的一部分,所以Client B发往Client A的SYN包会顺利地通过Client A的NAT设备,到达Client A,从而建立起Client A与Client B的P2P连接。

TCP同时打开
有一种特殊的TCP P2P打洞场景:假定各终端的TCP连接启动时间比较巧合,使得他们各自发送的 SYN报文,在到达对方的NAT设备之前,对方的SYN报文都已经 穿越NAT设备,并在NAT设备上生成TCP映射关系。在这种“幸 运”的情况下,NAT设备不会拒绝SYN报文,双方的SYN报文都 能通过终端间的NAT设备,到达对方。此时,终端会发现TCP连 接同时打开,每个终端的TCP返回SYN-ACK报文,报文的SYN 部分必须与之前发送的SYN报文一样,而ACK部分告知对方到达的SYN信息。

为什么这种方式可以建立TCP连接呢?这是因为TCP三次握手中有一种特殊的场景:通信双方同时在相同端口打开连接的场景,如图 6所示:

图 6 TCP同时打开连接

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

_kerneler

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值