P2P可以是一种通信模式、一种逻辑网络模型、一种技术、甚至一种理念。在P2P网络中,所有通信节点的地位都是对等的,每个节点都扮演着客户机和服务器双重角色,节点之间通过直接通信实现文件信息、处理器运算能力、存储空间等资源的共享。P2P网络具有分散性、可扩展性、健壮性等特点,这使得P2P技术在信息共享、即时通讯、协同工作、分布式计算、网络存储等领域都有广阔的应用。

由于主机可能位于防火墙或NAT之后,在进行P2P通信之前,我们需要进行检测以确认它们之间能否进行P2P通信以及如何通信。这种技术通常称为NAT穿透(NAT Traversal)。最常见的NAT穿透是基于UDP的技术。

1、应用层网关技术

NAT带来的问题之一就是限制了使用高层协议的两端P2P通信,因为NAT不允许外部的Peer节点主动连接或发送数据包给NAT后面的主机。ALG是解决NAT对应用层协议无感知的一个最常用方法。

ALG技术是利用NAT本身的支持来进行NAT的穿越,这个方案有很大限制,主要的原因是ALG都是为特定协议的特定规范版本而开发的,然而不管是协议本身,还是协议的数量都在变化,这就使得ALG适应性不强

2、中间件技术

这是一种通过开发通用方法解决NAT穿越问题的努力。与前者不同之处是,AGL技术中NAT网关是这一解决方案的唯一参与者,而中间件技术中客户端会参与网关公网映射信息的维护。UPnP就是这样一种方法,UPnP中文全称为通用即插即用,是一个通用的网络终端与网关的通信协议,具备信息发布和管理控制的能力。

3、打洞技术

Hole Punching技术是工作在运输层的技术,可以屏蔽上层应用层的差异,并且不需要NAT网关特定的支持,因此其通用性比较强,应用性也比较广。

3.1、反向链接技术

适用场景:通信双方只有一方位于NAT设备之后

客户端A位于NAT之后,NAT设备重新分配了TCP端口62000,由于B拥有外网地址,A可以直接通过TCP链接到B。但B想要与A通信,就需要通过服务器给A转发一个连接请求,反过来请求A连接到B(即反向链接),A在收到从服务器转发过来的请求以后,会主动向B发起一个连接请求,这样在NAT设备上就会建立起关于这个连接的相关表项,使AB之间能够正常通信,建立TCP连接。

3.2、基于UDP协议的P2P打洞技术

3.2.1、原理

UDP打洞技术是通过中间服务器的协助在各自的NAT网关上建立相关的表项,使P2P连接的双方发送的报文能够直接穿透对方的NAT网关,从而实现P2P客户端互连。如果两台位于NAT设备后面的P2P客户端希望在自己的NAT网关上打个洞,那么他们需要一个协助者——集中服务器,并且还需要一种用于打洞的Session建立机制。

集中服务器本质上是一台被设置在公网上的服务器,建立P2P的双方都可以直接访问到这台服务器。 位于NAT网关后面的客户端A和B都可以与一台已知的集中服务器建立连接,并通过这台集中服务器了解对方的信息并中转各自的信息。 同时集中服务器的另一个重要作用在于判断某个客户端是否在NAT网关之后,集中服务器可以从客户端的登陆消息中得到该客户端的内网相关信息,还可以通过登陆消息的IP头和UDP头得到该客户端的外网相关信息。如果该客户端不是位于NAT设备后面,那么采用上述方法得到的两对地址二元组信息是完全相同的。

P2P的Session建立的原理就是通过集中服务器将包含各自的内外网二元组发送给对方,这样彼此都能知道对方内外网的地址了。然后A开始向B的内外网地址发送UDP数据包,并且A会自动锁定第一个给出相应的B的地址的二元组。

3.2.2、场景一:两客户端位于同一NAT设备后(相同内网)

当A向集中服务器发出消息请求与B进行连接,集中服务器将B的外网地址二元组以及内网地址二元组发给A,同时把A的外网以及内网的地址二元组信息发给B。A和B发往对方公网地址二元组信息的UDP数据包不一定会被对方收到,这取决于当前的NAT设备是否支持不同端口之间的UDP数据包能否到达(即Hairpin转换特性),无论如何A与B发往对方内网的地址二元组信息的UDP数据包是一定可以到达的,内网数据包不需要路由,且速度更快。A与B推荐采用内网的地址二元组信息进行常规的P2P通信。

就目前的网络情况而言,应用程序在“打洞”的时候,最好还是把外网和内网的地址二元组都尝试一下。如果都能成功,优先以内网地址进行连接。

Hairpin技术需要NAT网关支持,它能够让两台位于同一台NAT网关后面的主机,通过对方的公网地址和端口相互访问。目前有很多NAT设备不支持该技术,这种情况下,NAT网关在一些特定场合下将会阻断P2P穿越NAT的行为,打洞的尝试是无法成功的。

3.2.3、场景二:两客户端位于不同NAT设备后面(不同内网)

在客户端向服务器发送的登陆消息中,包含有客户端的内网地址二元组信息;服务器会记录下客户端的内网地址二元组信息,同时会把自己观察到的客户端的外网地址二元组信息记录下来。

无论A、B二者中任何一方向服务器发送P2P连接请求,服务器都会将起记录下来内外网地址发送给A和B。

A拿到B的外网地址后,开始发送消息,这个过程就是“打洞”。如果A发给B的外网地址二元组的消息包在B向A发送消息包之前到达B的NAT设备,B的NAT设备会认为A发过来的消息是未经授权的外网消息,并丢弃该数据包,B发往A的消息包也会在B的NAT设备上建立一个【10.10.1.3:4321 155.99.25.11:62000】会话。

一旦A与B都向对方的NAT设备在外网上的地址二元组发送了数据包,就打开了A与B之间的“洞”,A与B向对方的外网地址发送数据,等效为向对方的客户端直接发送UDP数据包了。一旦应用程序确认已经可以通过往对方的外网地址发送数据包的方式让数据包到达NAT后面的目的应用程序,程序会自动停止继续发送用于“打洞”的数据包,转而开始真正的P2P数据传输。

3.2.4、场景三:两客户端位于两层(或多层)NAT设备之后(不同内网)

此种情景最典型的部署情况就像这样:最上层的NAT设备通常是由网络提供商(ISP)提供,下层NAT设备是家用路由器。

从这种拓扑结构上来看,只有服务器与NAT C是真正拥有公网可路由IP地址的设备,而NAT A和NAT B所使用的公网IP地址,实际上是由ISP服务提供商设定的(相对于NAT C而言)内网地址(我们将这种由ISP提供的内网地址称之为“伪”公网地址)。

最优化的路由策略当然是通过“伪公网”IP通信,但不幸的是A、B是无法通过服务器知道这些“伪公网”地址,即使知道,也不建议,因为这些地址是由ISP提供的,存在重复的可能性(例如:NAT A的内网的IP地址域恰好与NAT A在NAT C的“伪”公网IP地址域重复,这样就会导致打洞数据包无法发出的问题)。

因此客户端只能使用公网服务器观察到的A、B的公网地址进行“打洞”操作,用于“打洞”的数据包将由NAT C进行转发。

3.3、基于TCP协议的P2P打洞技术

这种技术从协议层面和UDP类似,只要NAT设备支持的话,基于TCP的P2P技术的健壮性将比基于UDP技术的更强一些,因为TCP协议的状态机给出了一种标准的方法来精确的获取某个TCP session的生命期,而UDP协议则无法做到这一点。

3.3.1、套接字和TCP端口重用

API不提供类似UDP那样的,同一个端口既可以向外连接,又能够接受来自外部的连接。

TCP的套接字通常仅允许建立1对1的响应,即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败。

为了能够打洞顺利,需要使用一个TCP端口监听来自外部分TCP连接,同时建立多个向外的TCP连接。幸运的是,所有主流操作系统支持特殊的TCP套接字参数SO_REUSEADDR

3.3.2、打开P2P的TCP流

假定客户端A希望建立与B的TCP连接。我们像通常一样假定A和B已经与公网上的已知服务器建立了TCP连接。服务器记录下来每个接入的客户端的公网和内网的地址二元组,如同为UDP服务的时候一样。

从协议层面来看,两者过程几乎完全相同。

与UDP不同的是,因为使用UDP协议的每个客户端只需要一个套接字即可完成与服务器的通信,而TCP客户端必须处理多个套接字绑定到同一个本地TCP端口的问题。

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

3.3.3、从应用程序角度来看TCP打洞

假定A首先向B发出SYN包,该包发往B的公网地址二元组,并且被B的NAT设备丢弃,但是B发往A的公网地址二元组的SYN包则通过A的NAT到达了A,然后,会发生以下的两种结果中的一种,具体是哪一种取决于操作系统对TCP协议的实现:

  • A会认为自己连接成功了,本来A要找B,结果B自己送上门了,此时connect()会成功返回,A的listen()等待从外部联入的函数将没有任何反应。此时,B联入A的操作在A程序的内部被理解为A联入B连接成功,A的TCP将用SYN-ACK包回应B,B也认为自己连接成功了,连接建立起来。
  • A没有前面那么聪明,没有发现联入的B就是自己希望的。A通过常规的listen()函数和accept()函数得到与B的连接,而由A发起的向B的公网地址二元组连接将以失败告终。

第一种结果适用于基于BSD的操作系统对于TCP的实现,而第二种结果更加普遍一些,多数Linux和Windows系统都会按照第二种结果来处理。

参考链接:

Peer-to-Peer Communication Across Network Address Translators

http://www.52im.net/thread-1055-1-1.html

http://www.52im.net/thread-2872-1-1.html

P2P技术(2)——NAT穿透的更多相关文章

  1. P2P技术简介

    P2P技术简介 NAT( Network Address Translation)穿越(俗称打洞)技术 前言: p2p已经存在于我们生活的方方面面:我们通过下载在工具(比如迅雷,bitorent,各种 ...

  2. NAT穿透进行P2P文件传输

    实现一个简单的p2p文件传输,主要解决NAT穿透问题,使用tcp协议传输. NAT背景介绍 简介 NAT(Network Address Translation ,网络地址转换) 是一种广泛应用的解决 ...

  3. P2P技术详解(二):P2P中的NAT穿越(打洞)方案详解

    1.内容概述 P2P即点对点通信,或称为对等联网,与传统的服务器客户端模式(如下图"P2P结构模型"所示)有着明显的区别,在即时通讯方案中应用广泛(比如IM应用中的实时音视频通信. ...

  4. 《c# 实现p2p文件分享与传输系统》 二、 设计 - 续(NAT穿透)

    c#实现P2P文件分享与传输系统 二.设计 - 续(NAT穿透) 首先要抱歉,因为这些日子较忙,没有写文章,这个系列拖了很久,现在开始继续.  上一篇文章介绍了p2p系统Tracker Server和 ...

  5. P2P技术(一):NAT

    1.NAT由来 NAT是一项神奇的技术,说它神奇在于它的出现几乎使IPv4起死回生.在IPv4已经被认为行将结束历史使命之后近20年时间里,人们几乎忘了IPv4的地址空间即将耗尽这样一个事实--在新技 ...

  6. 使用TCP协议的NAT穿透技术

    一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基本上没有人会告诉你如何使用TCP协议去穿透(甚至有的人会直接告诉你TCP协议是无法实现穿透的).但是,众所周知的是,UDP是一个无连 ...

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

    1. IPv4协议和NAT的由来 今天,无数快乐的互联网用户在尽情享受Internet带来的乐趣.他们浏览新闻,搜索资料,下载软件,广交新朋,分享信息,甚至于足不出户获取一切日用所需.企业利用互联网发 ...

  8. 使用TCP协议的NAT穿透技术(转)

    其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之. 一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基 ...

  9. 使用TCP协议的NAT穿透技术 (转载)

    其实很早我就已经实现了使用TCP协议穿透NAT了,但是苦于一直没有时间,所以没有写出来,现在终于放假有一点空闲,于是写出来共享之. 一直以来,说起NAT穿透,很多人都会被告知使用UDP打孔这个技术,基 ...

随机推荐

  1. 使用UltraISO制作ubuntu安装u盘启动盘图文教程

    使用UltraISO制作ubuntu安装u盘启动盘图文教程 胖先森关注 0.9572017.09.07 11:06:15字数 770阅读 27,901   制作U盘启动1.gif 1.首先打开Ultr ...

  2. UltraISO制作启动盘安装CentOS7

    UltraISO制作启动盘安装CentOS7 发表于 2020-03-10  |  分类于 DevOps  |  没有评论 简单几个步骤即可完成启动盘的制作,非常便利 准备工具 准备8G优盘(启动盘制 ...

  3. ipmitool使用手册

    ipmitool使用手册原创xinqidian_xiao 最后发布于2018-07-05 12:15:47 阅读数 17948 收藏展开一.查找安装包 查看ipmitool属于哪个安装包 #yum p ...

  4. "sar"工具 利用率

    LTP--linux稳定性测试 linux性能测试 ltp压力测试   余二五 2017-11-14 16:20:00 浏览1172 linux 日志 配置 内存管理 测试 脚本 性能测试 压力测试 ...

  5. 启动dubbo消费端过程提示No provider available for the service的问题定位与解决

    文/朱季谦 某次在启动dubbo消费端时,发现无法从zookeeper注册中心获取到所依赖的消费者API,启动日志一直出现这样的异常提示 Failed to check the status of t ...

  6. Locust入门

    Locust入门   Locust是一款Python技术栈的开源的性能测试工具.Locust直译为蝗虫,寓意着它能产生蝗虫般成千上万的并发用户: Locust并不小众,从它Github的Star数量就 ...

  7. Docker学习(14) Docker容器的数据管理

    Docker容器的数据管理 Docker容器的数据卷 重要: Docker的数据卷容器 Docker数据卷的备份和还原

  8. 用NumPy genfromtxt导入数据

    用NumPy genfromtxt导入数据 NumPy provides several functions to create arrays from tabular data. We focus ...

  9. 车载智能HUD

    车载智能HUD 从 HUD 到行车记录仪再到后视镜,最近有不少团队都发布了自己的车载智能硬件. 什么是 HUD? HUD 全称抬头数字显示仪 (Heads Up Display),又叫平视显示系统,简 ...

  10. CodeGen结构循环回路

    CodeGen结构循环回路 structure循环是一个模板文件构造,它允许您迭代CodeGen拥有的有关结构的集合.为了使用结构循环,必须同时基于多个存储库结构生成代码. CodeGen可以通过以下 ...