P2P技术原理及相关利弊
引言
P2P技术是近几年来发展较快的技术,它直接将人们联系起来,让人们通过互联网直接交互。P2P使得网络上的沟通变得容易、更直接共享和交互,真正地消除中间商。人们可以直接连接到其他用户的计算机、交换文件,而不是像过去那样连接到服务器去浏览与下载。
1. P2P技术原理
P2P技术属于覆盖层网络的范畴,是相对于客户机/服务器(C/S)模式来说的一种网络信息交换方式。在C/S模式中,数据的分发采用专门的服务器,多个客户端都从此服务器获取数据。这种模式的优点是:数据的一致性容易控制,系统也容易管理。但是此种模式的缺点是:因为服务器的个数只有一个,系统容易出现单一失效点;单一服务器面对众多的客户端,由于CPU能力、内存大小、网络带宽的限制,可同时服务的客户端非常有限,可扩展性差。P2P技术正是为了解决这些问题而提出来的一种对等网络结构。在P2P网络中,每个节点既可以从其他节点得到服务,也可以向其他节点提供服务。这样,庞大的终端资源被利用起来,一举解决了C/S模式中的两个弊端。
P2P网络有3种比较流行的组织结构,被应用在不同的P2P应用中。
1.1分布式哈希表结构
分布式哈希表结构[1]是一种功能强大的工具,它的提出引起了学术界一股研究DHT的热潮。虽然DHT具有各种各样的实现方式,但是具有共同的特征,即都是一个环行拓扑结构,在这个结构里每个节点具有一个唯一的节点标识(ID),节点ID是一个128位的哈希值。每个节点都在路由表里保存了其他前驱、后继节点的ID。如图1(a)所示。通过这些路由信息,可以方便地找到其他节点。这种结构多用于文件共享和作为底层结构用于流媒体传输。
1.2树形结构
P2P网络树形结构如图1(b)所示。在这种结构中,所有的节点都被组织在一棵树中,树根只有子节点,树叶只有父节点,其他节点既有子节点也有父节点。信息的流向沿着树枝流动。最初的树形结构多用于P2P流媒体直播。
1.3网状结构
网状结构如图1(c)所示,又叫无结构。顾名思义,这种结构中,所有的节点无规则地连在一起,没有稳定的关系,没有父子关系。网状结构为P2P提供了最大的容忍性、动态适应性,在流媒体直播和点播应用中取得了极大的成功。当网络变得很大时,常常会引入超级节点的概念,超级节点可以和任何一种以上结构结合起来组成新的结构,如KaZaA。[1]
2.P2P技术特点
2.1非中心化
网络中的资源和服务分散在所有结点上,信息的传输和服务的实现都直接在结点之间进行,可以无需中间环节和服务器的介入,避免了可能的瓶颈。P2P的非中心化基本特点,带来了其在可扩展性、健壮性等方面的优势。
2.2可扩展性
在P2P网络中,随着用户的加入,不仅服务的需求增加了,系统整体的资源和服务能力也在同步地扩充,始终能比较容易地满足用户的需要。理论上其可扩展性几乎可以认为是无限的。例如:在传统的通过FTP的文件下载方式中,当下载用户增加之后,下载速度会变得越来越慢,然而P2P网络正好相反,加入的用户越多,P2P网络中提供的资源就越多,下载的速度反而越快。
2.3健壮性
P2P架构天生具有耐攻击、高容错的优点。由于服务是分散在各个结点之间进行的,部分结点或网络遭到破坏对其它部分的影响很小。P2P网络一般在部分结点失效时能够自动调整整体拓扑,保持其它结点的连通性。P2P网络通常都是以自组织的方式建立起来的,并允许结点自由地加入和离开。
2.4高性价比
性能优势是P2P被广泛关注的一个重要原因。随着硬件技术的发展,个人计算机的计算和存储能力以及网络带宽等性能依照摩尔定理高速增长。采用P2P架构可以有效地利用互联网中散布的大量普通结点,将计算任务或存储资料分布到所有结点上。利用其中闲置的计算能力或存储空间,达到高性能计算和海量存储的目的。目前,P2P在这方面的应用多在学术研究方面,一旦技术成熟,能够在工业领域推广,则可以为许多企业节省购买大型服务器的成本。
2.5隐私保护
在P2P网络中,由于信息的传输分散在各节点之间进行而无需经过某个集中环节,用户的隐私信息被窃听和泄漏的可能性大大缩小。此外,目前解决Internet隐私问题主要采用中继转发的技术方法,从而将通信的参与者隐藏在众多的网络实体之中。在传统的一些匿名通信系统中,实现这一机制依赖于某些中继服务器节点。而在P2P中,所有参与者都可以提供中继转发的功能,因而大大提高了匿名通讯的灵活性和可靠性,能够为用户提供更好的隐私保护。
2.6负载均衡
P2P 网络环境下由于每个节点既是服务器又是客户机,减少了对传统C/S结构服务器计算能力、存储能力的要求,同时因为资源分布在多个节点,更好的实现了整个网络的负载均衡。[2]
3.P2P技术缺陷
3.1伪造数据
在P2P网络中,都需要一个描述文件信息的Metadata数据,该数据包含了要下载文件的分块大小和每块数据的完整性检验值,以及Tracker服务器的地址。 而Tracker在文件的P2P网络传输过程中,跟踪P2P节点拥有的文件块信息,用于其他节点及时获取拥有需要的内容块的其他节点的地址。
在破解了P2P协议的情况下,黑客能够在P2P网络中传递Metadata数据时,将其修改为另外一个伪造的文件信息,并同时修改Tracker服务器的地址,同时用伪造的Tracker服务器代替原来的Tracker。那么,P2P网络中的节点会向伪造的Tracker服务器查询P2P网络中其他节点,Tracker服务器会把拥有伪造数据的节点信息发送给该节点。这些节点就会下载到伪造的数据中。这些数据在最后进行完整性检验的时候,也不能被发现出来,因为Metadata中文件的检验信息就是该伪造数据的信息。
P2P网络中由于缺乏统一的管理,在发现伪造的数据后,不能控制P2P节点不下载伪造的数据或者阻止P2P网络中伪造数据的传输,从而导致伪造的数据在P2P网络中肆意传播,且会处于完全失控状态。
在P2P网络中,由于任何节点都是可以加入的,同时也为网络中的其他节点提供转发服务。从网络攻击的角度来看,一个“恶意”节点故意将正常的数据替换成伪造的数据,在转发的整个P2P网络中,完全可以欺骗其他P2P的节点而不被发现。
3.2易受攻击
从主流P2P实现来看,虽然数据共享与传送是在用户节点之间直接进行,但在大规模的网络应用中都选择了构建索引服务器进行资源查询与定位。例如,BT应用中设置了Tracker服务器,用户通过资源描述的Torrent 文件得到Tracker的地址之后,连接到Tracker,得到用户节点列表。eMule的客户端通过内置的Tracker地址列表能实现资源的搜索与查找。当然,在KazaA模型中的超级节点也充当了索引服务器的角色。目前,许多网络黑客不约而同地选择了以上的索引服务器连接过程,作为攻击服务器的入手点。P2P的众多用户无形之中成为了网络攻击的发起者。
假定攻击者通过其他途径(例如木马、病毒、网络信息截获、端口扫描)获取了某个服务器的端口信息,就可以欺骗P2P用户对服务器发动拒绝服务攻击DDoS。这里的欺骗手段是多样化的:在局域网内可通过ARP欺骗的方式;BT应用中客户端通过HTTP的方式连接到Tracker,随后发起Get_peerList的会话。因此,攻击者可以伪装一个虚假的Tracker服务器,设置热门资源的虚假PeerList信息指向目标服务器的端口。由于PeerList信息指出热门资源在目的服务器上,则上千台用户计算机尝试与目标服务器进行连接,从而实现了拒绝服务攻击。
DDoS攻击一直是网络安全的一个难题。传统的DDoS攻击需要伪造数据包或者控制大规模的计算机来发动。在P2P规模日益扩大的今天,攻击者不需具体入侵用户终端,就能毫不费力地借助网络中的P2P用户发动攻击,这给网络安全提出了新的挑战。
事实上,通过欺骗P2P客户端不但能形成大规模的网络攻击,而且这种情况下大量用户始终处于请求连接的状态,使原有的P2P应用也受到了很大影响。例如,许多P2P流媒体应用中采用了预置索引服务器的方式,受到以上重定向手段的影响后,用户将无法获取到正确的节点列表,从而无法获得媒体服务。同时在用户节点发起数据分片请求时,如果伪装的热点资源服务器伪造数据包进行响应,这使得用户节点接收到的数据分片总是解码失败。
3.3信息泄露
P2P技术使得用户信息和私有网络信息的安全性面临挑战。通常企业或者用户都需要构建自己的私有网络,公网用户不能直接与私网用户建立直接连接进行数据交换。入侵者总是要想方设法得到私有网络内部的信息,通常都是采用带毒邮件、网络插件携带木马等方式使私网内部的用户在不知情的状态下泄漏内部网络的信息。
P2P软件的盛行,使这一过程变得更加难以控制。P2P软件通常都具备私网穿越的功能。例如BT客户端在连接Tracker的时候,请求消息中客户端会对网络地址进行判断;如果客户端在私网内,客户端就会将私网用户的私网地址、端口号,及其所对应的公网地址、端口号包含在消息体中进行发送。
通过私有网络的信息,入侵者可以对内部网络进行有效地攻击。P2P技术的迅速发展,使得恶意P2P软件形成了入侵的有效手段。恶意P2P软件在共享本地资源的过程中,用户无法察觉到它将自己的私密信息发送到入侵者处。而且在P2P的环境中,追踪入侵者的网络位置变得十分困难。
P2P使得网络热门资源能够在网络中迅速传播,这给病毒和恶意软件提供了传播的捷径。当然,P2P软件本身有可能是无害的,但共享的文件中却可能因为存在漏洞而被利用。流行的RMVB格式存在一个广告弹出的漏洞,这被许多不法分子所利用,作散布不良信息的渠道。热门影片的用户关注程度总是比较高,不法分子在影片中携带弹出式广告,使得有害信息在P2P网络上爆炸式地传播,远远超过了在传统C/S模式下的传播速度。
近来一段时间,蠕虫病毒开始在P2P网络中泛滥,KazaA便首当其冲。例如,Worm.P2P.SpyBot 蠕虫病毒通过KazaA传播,也可通过被后门程序感染的计算机传播。P2P网络节点众多,只要有一个节点感染病毒,就能够通过P2P通信机制将病毒迅速扩散到逻辑邻近的节点,而且逻辑邻近节点在物理上可能分布于多个网络区域,这使得短时间内对网络用户造成的破坏程度远远超过以前。[3]
4.结论
P2P技术发展至今,给人们带来了许多便利,它直接将人们联系在了一起,让人们能够通过互联网进行交流。然而随之产生的一系列安全隐患也值得我们深思,相信在不久的将来,这些技术缺陷都能一一解决,从而使整个P2P技术得以提升、完善和发展,可以说,P2P的技术前景一片光明。
参考文献
[1]金海 廖小飞 P2P技术原理及应用 《中兴通讯技术》20##年13卷6期.
[2]罗杰文 Peer-To-Peer综述 中科院计算技术研究所http://www.intsci.ac.cn/users/luojw/P2P/ 20##-4-25.
[3]网名萤火虫 P2P技术缺陷http://qzone.qq.com/blog/94002708-1234492098 20##-2-13.
第二篇:P2P技术穿透防火墙及代码的简单实现
P2P技术穿透防火墙及代码的简单实现
首先先介绍一些基本概念:
NAT(Network Address
Translators),网络地址转换:网络地址转换是在IP地址日益缺乏的情况下产生的,它的主要目的就是为了能够地址重用。NAT分为两大类,基本的NAT和NAPT(Network
Address/Port Translator)。
最开始NAT是运行在路由器上的一个功能模块。
最先提出的是基本的NAT,它的产生基于如下事实:一个私有网络(域)中的节点中只有很少的节点需要与外网连接(呵呵,这是在上世纪90年代中期提出的)。那么这个子网中其实只有少数的节点需要全球唯一的IP地址,其他的节点的IP地址应该是可以重用的。
因此,基本的NAT实现的功能很简单,在子网内使用一个保留的IP子网段,这些IP对外是不可见的。子网内只有少数一些IP地址可以对应到真正全球唯一的 IP地址。如果这些节点需要访问外部网络,那么基本NAT就负责将这个节点的子网内IP转化为一个全球唯一的IP然后发送出去。(基本的NAT会改变IP 包中的原IP地址,但是不会改变IP包中的端口)
关于基本的NAT可以参看RFC 1631
另外一种NAT叫做NAPT,从名称上我们也可以看得出,NAPT不但会改变经过这个NAT设备的IP数据报的IP地址,还会改变IP数据报的 TCP/UDP端口。基本NAT的设备可能我们见的不多(呵呵,我没有见到过),NAPT才是我们真正讨论的主角。看下图:
Server S1
18.181.0.31:1235
|
^ Session 1 (A-S1) ^ |
| 18.181.0.31:1235 | |
v 155.99.25.11:62000 v |
|
NAT
155.99.25.11
|
^ Session 1 (A-S1) ^ |
| 18.181.0.31:1235 | |
v 10.0.0.1:1234 v |
|
Client A
10.0.0.1:1234
有一个私有网络10.*.*.*,Client
A是其中的一台计算机,这个网络的网关(一个NAT设备)的外网IP是155.99.25.11(应该还有一个内网的IP地址,比如10.0.0.10)。如果Client
A中的某个进程(这个进程创建了一个UDP
Socket,这个Socket绑定1234端口)想访问外网主机18.181.0.31的1235端口,那么当数据包通过NAT时会发生什么事情呢?
首先NAT会改变这个数据包的原IP地址,改为155.99.25.11。接着NAT会为这个传输创建一个Session(Session是一个抽象的概念,如果是TCP,也许Session是由一个SYN包开始,以一个FIN包结束。而UDP呢,以这个IP的这个端口的第一个UDP开始,结束呢,呵呵,也许是几分钟,也许是几小时,这要看具体的实现了)并且给这个Session分配一个端口,比如62000,然后改变这个数据包的源端口为62000。所以本来是
(10.0.0.1:1234->18.181.0.31:1235)的数据包到了互联网上变为了
(155.99.25.11:62000->18.181.0.31:1235)。
一旦NAT创建了一个Session后,NAT会记住62000端口对应的是10.0.0.1的1234端口,以后从18.181.0.31发送到 62000端口的数据会被NAT自动的转发到10.0.0.1上。(注意:这里是说18.181.0.31发送到62000端口的数据会被转发,其他的 IP发送到这个端口的数据将被NAT抛弃)这样Client A就与Server S1建立以了一个连接。
呵呵,上面的基础知识可能很多人都知道了,那么下面是关键的部分了。
看看下面的情况:
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
接上面的例子,如果Client A的原来那个Socket(绑定了1234端口的那个UDP Socket)又接着向另外一个Server
S2发送了一个UDP包,那么这个UDP包在通过NAT时会怎么样呢? 这时可能会有两种情况发生,一种是NAT再次创建一个Session,并且再次为这个Session分配一个端口号(比如:62001)。另外一种是 NAT再
次创建一个Session,但是不会新分配一个端口号,而是用原来分配的端口号62000。前一种NAT叫做Symmetric
NAT,后一种叫做Cone
NAT。我们期望我们的NAT是第二种,呵呵,如果你的NAT刚好是第一种,那么很可能会有很多P2P软件失灵。(可以庆幸的是,现在绝大多数的NAT属于后者,即Cone
NAT)
好了,我们看到,通过NAT,子网内的计算机向外连结是很容易的(NAT相当于透明的,子网内的和外网的计算机不用知道NAT的情况)。
但是如果外部的计算机想访问子网内的计算机就比较困难了(而这正是P2P所需要的)。
那么我们如果想从外部发送一个数据报给内网的计算机有什么办法呢?首先,我们必须在内网的NAT上打上一个“洞”(也就是前面我们说的在NAT上建立一个 Session),这个洞不能由外部来打,只能由内网内的主机来打。而且这个洞是有方向的,比如从内部某台主机(比如:192.168.0.10)向外部的某个IP(比如:219.237.60.1)发送一个UDP包,那么就在这个内网的NAT设备上打了一个方向为219.237.60.1的“洞”,(这就是称为UDP
Hole
Punching的技术)以后219.237.60.1就可以通过这个洞与内网的192.168.0.10联系了。(但是其他的IP不能利用这个洞)。
呵呵,现在该轮到我们的正题P2P了。有了上面的理论,实现两个内网的主机通讯就差最后一步了:那就是鸡生蛋还是蛋生鸡的问题了,两边都无法主动发出连接请求,谁也不知道谁的公网地址,那我们如何来打这个洞呢?我们需要一个中间人来联系这两个内网主机。
现在我们来看看一个P2P软件的流程,以下图为例:
Server S (219.237.60.1)
|
|
+----------------------+----------------------+
| |
NAT A (外网IP:202.187.45.3) NAT B (外网IP:187.34.1.56) | (内网IP:192.168.0.1) | (内网IP:192.168.0.1)
| |
Client A (192.168.0.20:4000) Client B (192.168.0.10:40000) 首先,Client A登录服务器,NAT A为这次的Session分配了一个端口60000,那么Server S收到的Client
A的地址是202.187.45.3:60000,这就是Client A的外网地址了。同样,Client B登录Server S,NAT
B给此次Session分配的端口是40000,那么Server S收到的B的地址是187.34.1.56:40000。
此时,Client A与Client B都可以与Server S通信了。如果Client
A此时想直接发送信息给Client
B,那么他可以从Server S那儿获得B的公网地址
187.34.1.56:40000,是不是Client
A向这个地址发送信息Client B就能收到了呢?答案是不行,因为如果这样发送信息,NAT
B会将这个信息丢弃(因为这样的信息是不请自来的,为了安全,大多数NAT都会执行丢弃动作)。现在我们需要的是在NAT
B上打一个方向为202.187.45.3(即Client A的外网地址)的洞,那么Client
A发送到187.34.1.56:40000的信息,Client B就能收到了。这个打洞命令由谁来发呢,呵呵,当然是Server S。
总结一下这个过程:如果Client A想向Client B发送信息,那么Client A发送命令给Server S,请求Server
S命令Client B向Client
A方向打洞。呵呵,是不是很绕口,不过没关系,想一想就很清楚了,何况还有源代码呢(侯老师说过:在源代码面前没有秘密
8)),然后Client A就可以通过Client B的外网地址与Client B通信了。
注意:以上过程只适合于Cone NAT的情况,如果是Symmetric NAT,那么当Client B向Client
A打洞的端口已经重新分配了,Client B将无法知道这个端口(如果Symmetric
NAT的端口是顺序分配的,那么我们或许可以猜测这个端口号,可是由于可能导致失败的因素太多,我们不推荐这种猜测端口的方法)。 另一篇文章接上:
下面解释一下上面的文章中没有提及或者说我觉得比较欠缺的地方.
私有地址/端口和公有地址/端口:我们知道,现在大部分网络采用的都是NAPT(Network Address/Port Translator)了, 这个东东的作用是一个对外的对话在经过NAT之后IP地址和端口号都会被改写,在这里把一次会话中客户自己认为在使用的IP地址和端口号成为私有地址/端口,而把经过NAPT之后被改写的IP地址和端口号称为公有地址/端口.或者可以这么理解,私有地址/端口是你家里人对你的昵称而公有地址/端口则是你真正对外公开的名字.如何获得用户的私用地址/端口号,这个很简单了,而要得到公有地址/端口号就要在连接上另一台机器之后由那台机器看到的IP地址和端口号来表示.
如果明白了上面的东西,下面进入我们的代码,在这里解释一下关键部分的实现:
客户端首先得到自己的私有地址/终端,然后向server端发送登陆请求,server端在得到这个请求之后就可以知道这个client端的公有地址/终端,server会为每一个登陆的client保存它们的私有地址/端口和公有地址/端
口.
OK,下面开始关键的打洞流程.假设client A要向client B对话,但是A不知道B的地址,即使知道根据NAT的原理这个对话在第一次会被拒绝,因为client B的NAT认为这是一个从没有过的外部发来的请求.这个时候,A如果发现自己没有保存B的地址,或者说发送给B的会话请求失败了, 它会要求server端让B向A打一个洞,这个B->A的会话意义在于它使NAT B认为A的地址/端口是可以通过的地址/端口,这样A再向B发送对话的时候就不会再被
NAT B拒绝了.打一个比方来说明打洞的过程,A想来B家做客,但是遭到了B的管家NAT B的拒绝,理由是:我从来没有听我家B 提过你的名字,这时A找到了A,B都认识的朋友server,要求server给B报一个信,让B去跟管家说A是我的朋友,于是,B跟管家NAT B 说,A是我认识的朋友,这样A的访问请求就不会再被管家NAT B所拒绝了.简而言之,UDP打洞就是一个通过server保存下来的地址使得彼此之间能够直接通信的过程,server只管帮助建立连接,在建立间接之后就不再介入了.
下面是一个模拟P2P聊天的过程的源代码,过程很简单,P2PServer运行在一个拥有公网IP的计算机上,P2PClient运行在两个不同的NAT 后(注意,如果两个客户端运行在一个NAT后,本程序很可能不能运行正常,这取决于你的NAT是否支持loopback
translation,详见http://midcom-/draft-ford-midcom-p2p-01.txt ,当然,此问题可以通过双方先尝试连接对方的内网IP来解决,但是这个代码只是为了验证原理,并没有处理这些问题),后登录的计算机可以获得先登录计算机的用户名,后登录的计算机通过send
username message的格式来发送消息。如果发送成功,说明你已取得了直接与对方连接的成功。
程序现在支持三个命令:send , getu , exit
send格式:send username message
功能:发送信息给username
getu格式:getu
功能:获得当前服务器用户列表
exit格式:exit
功能:注销与服务器的连接(服务器不会自动监测客户是否吊线) 代码很短,相信很容易懂,如果有什么问题,可以给我发邮件zhouhuis22@sina.com
或者在CSDN上发送短消息。同时,欢迎转发此文,但希望保留作者版权8-)。
_05/04052509317298.rar"
/upload/2004_05/04052509317298.rar
另一篇介绍打洞技术的(补充)
UDP打洞技术依赖于由公共防火墙和cone NAT,允许适当的有计划的端对端应用程序通过NAT"打洞",即使当双方的主机都处于NAT之后。这种技术在 RFC3027的5.1节[NAT PROT] 中进行了重点介绍,并且在Internet[KEGEL]中进行了非正式的描叙,还应用到了最新的一些协议,例如[TEREDO,ICE]协议中。不过,我们要注意的是,"术"如其名,UDP打洞技术的可靠性全都要依赖于UDP。
这里将考虑两种典型场景,来介绍连接的双方应用程序如何按照计划的进行通信的,第一种场景,我们假设两个客户端都处于不同的NAT之后;第二种场景,我们假设两个客户端都处于同一个NAT之后,但是它们彼此都不知道(他们在同一个NAT中)。
处于不同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的会话。
假如这个时候 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来牵线搭桥了。(这就是所谓的打洞技术)!