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. javaWeb——Servlet(二)

    Servelet登录页面步骤: 浏览器访问http://127.0.0.1/login.html 浏览器通过form把账号和密码提交到/login(通过action),附带method="p ...

  2. 通过format学习,python的内部方法是面象对象的-python面向对象

    1.常用的形式 s ="{0} {0} qqq {0} xxx {1}".format('dog','cat')print(s) 结果:dog dog qqq dog xxx ca ...

  3. Flume 常用配置项

    注:以下配置项均为常见配置项,查询详细配置项可以访问 flume 官网 Source 常见配置项 Avro Source 配置项名称 默认值 描述 Channel – type – 组件类型名称,必须 ...

  4. Jira&Confluence服务器安装

    1.Mysql安装 参考https://confluence.atlassian.com/doc/database-setup-for-mysql-128747.html 创建相应的数据库 CREAT ...

  5. linux基础之权限管理

    本节内容 1. 权限类别 属主(owner) 属组(group) 其他人(other) 2. 查看权限 ls -l 十位: 第一位文件类型-,d,l, 3. 设置权限 chmod 选项 权限模式 fi ...

  6. 2.9高级变量类型操作(列表 * 元组 * 字典 * 字符串)_内置函数_切片_运算符_for循环

    高级变量类型 目标 列表 元组 字典 字符串 公共方法 变量高级 知识点回顾 Python 中数据类型可以分为 数字型 和 非数字型 数字型 整型 (int) 浮点型(float) 布尔型(bool) ...

  7. 用golang刷LeetCode

    用golang刷LeetCode 用Go语言刷LeetCode记录,只是为了练习Go语言,能力有限不保证都是最优解,只能在此抛转引玉了. 数据结构和算法 数据结构和算法是程序员的命根子,没了命根子也就 ...

  8. OpenResty搭建高性能服务端

    OpenResty搭建高性能服务端   Socket编程 Linux Socket编程领域为了处理大量连接请求场景,需要使用非阻塞I/O和复用,select.poll.epoll是Linux API提 ...

  9. Python+Selenium学习笔记18 - 不开启浏览器测试

    运行脚本时间比较长时可以不打开浏览器测试,这样在测试运行时,电脑还是可以用作其他操作的. 只需要在运行脚本上加上下面代码的678行即可 1 # coding = utf-8 2 3 from sele ...

  10. 根据某个数据 来筛选 DataTree 分支数里面的数据是否符合规则 进行筛选分支,展示

    问题:如何在 DataTree 树形数据里每个分支里根据特定某个元素值,然后挑选出来 思路:先把分支提取出来,每个都要进行判断. 主要用到的电池: >.Cull Pattern 拓展资料:以下是 ...