P2P通信技术
P2P是一个点对点通信框架,又称作对等联网,和传统的服务器—客户端模式存在着明显的差异。P2P广泛应用于即时通信中(实时音频通话、实时文件传输、实时文字聊天等),其通信节点地位是完全对等的,没有中心化的服务器,每个通信节点都扮演者客户端与服务器双重角色,各个节点间能够实现直接通信、共享算力资源与存储空间等。P2P的可扩展性强、鲁棒性高等特点使其能够满足信息共享、即时通信、分布式计算与存储等领域。
P2P支持端到端的通信,在复杂的网络环境中,通信节点位于NAT网关后的情况时常出现,这阻碍了P2P的功能。因此,NAT穿越技术是解决这个问题关键。
1 反向链接技术
NAT网关在受限制的类型下可能会阻挡外部设备发送的连接建立请求,为了解决1这一问题,我们基于中继服务器的方案,首先让通信双方相互知晓公网IP:端口,外部设备向中继服务器发送与A建立连接的请求,再由这个中继服务器向A转发连接一个请求,请求A发起连接B的请求。【中继服务器IP:端口号是NAT网关配置的不受限类型中的一员】【这个技术仅仅只能解决一小部分问题,而且中继服务器是通信的性能瓶颈】
2 基于UDP协议的打洞技术
通过中继服务器配置NAT网关,在NAT网关上建立相关的表项,使得报文能够直接穿过NAT网关。中继服务器部署在公网上,客户端能够访问它。【中继服务器和NAT穿透的STUN、TURN服务器类似,集成二者能力】。建立通信的过程如下:
假定客户端A想要与客户端B建立直接连接关系,A向中继服务器发送请求帮助与B的建立UDP连接。中继服务器将B的外网与内网的IP:端口号发送给A,同时把A的也发给B。当A收到中继服务器发的信息后,开始向B发送UDP数据包,并且A会自动锁定第一个给出响应的B的IP:端口号。同样的B也如此。【双方发送的UDP数据包无时序要求】
打洞的三个场景:
2.1 客户端位于同一台NAT网关后:(最简单)通过中继服务器获取对方的公网和私网IP:端口号,尝试两种地址连接:使用内网地址的连接【一定成功】和使用公网的地址连接【是否成功取决于NAT网关是否支持Hairpin技术,支持则会把A发给B公网的数据修改地址并通过B的私网地址转发给B】【实际上,对于同一私网的两台设备,应当设置内网地址连接优先级高于外网地址的连接,降低不必要的开销】
Hairpin技术:让同一私网的设备能够通过“公网地址”相互访问。乍一看没什么用处,但是其存在是为了解决通信初期两设备可能不知道自己是否在同一内网里(有多个192.168.1.0/24网络,即使A为192.168.1.1,B为192.168.1.2也不能确定双方在同一网络里),所以需要使用公网确认通信。还能够同一通信逻辑,彻底告别软件针对内网和外网设置不同的规则【如果出于性能考虑还是要设计两套规则】。兼容老旧设备,因为很多老旧设备只支持公网通信。进行多级NAT网关打洞。
2.2 客户端位于不同NAT设备后面:(最常见)中继服务器的作用依旧是提供双方的双重地址。之后进入打洞过程:A向B发送UDP数据包,NAT_A准许通过,NAT_B默认丢弃(因为还没有确认这个IP_A是否合法,确认也很简单,只要内部设备主动去向其发包),之后B向A发送UDP数据包,NAT_B准许通过,NAT_A此时准许通过【这里是因为之前A已经主动向B的公网IP发过包了,所以在有效期内都认为IP_B发给A的包合法】。到这里就打好洞了,双方可以依赖公网IP进行通信了。
2.3 客户端位于多层NAT设备后:(跨大规模网络同信会出现,简单来说两个不同运营商提供的WIFI被两个客户端连接,这两个客户端之间至少有三个NAT网关)打洞策略其实和2.2区别不大,不过出于多级NAT网关考虑,只有最外层的NAT网关拥有真实的公网IP,内部的NAT网关所拥有的称作伪公网地址。考虑以下的拓扑结构:
中继服务器只能看见NAT C上的公网地址。必须要求NAT C必须支持Hairpin技术。A向B发送打洞包[IP_A:Port_A,IP_PB:Port_PB],NAT A允许通过[IP_A_:Port_A_,IP_PB:Port_PB]。NAT C识别发现是发给自己的包,由于支持Hairpin技术,会将目的地址从真公网IP:端口号替换成B的伪公网IP:端口号[IP_PA:Port_PA,IP_B_:Port_B_]。NAT B识别为非法包。之后B向A发送打洞包[IP_B:Port_B,IP_PA:Port_PA],NAT B允许通过[IP_B_:Port_B_,IP_PA:Port_PA]。NAT C转发到NAT A上,NAT A已经有向真公网IP发送过请求,故认为合法,允许通过[IP_PB:Port_PB,IP_A_:Port_A_]。至此,打洞成功。【注意每穿过NAT设备都会替换源地址,支持Hairpin技术的还会将指向自身目的地址进行映射表转换】
在实际应用中,需要考虑UDP超时问题【NAT技术会对UDP通信采取老化操作】,因此,最好在打洞完成后设定一个穿越有效期或者在失效前再打一个洞等等。【有效期目前没有标准,建议采用发送心跳包的方式维护打出的洞,就是有点占据带宽】
3 基于TCP协议的打洞技术
TCP打洞和UDP打洞过程类似,尽管如此,TCP打洞的理论还是比较复杂,支持TCP打洞的NAT设备不多。TCP打洞技术比UDP更加可靠,因为TCP协议给出了通信的生命周期,可自主协商关闭连接,不必像UDP那样为了超时而设计各种维护方案。
3.1 套接字和TCP端口的重用:在实现TCP打洞时,应用程序的API接口不提供类似于UDP那样的同一端口既能向外连接,又能接受来自外部的连接。而且TCP的套接字通常仅允许建立一对一的响应【即应用程序在将一个套接字绑定到本地的一个端口以后,任何试图将第二个套接字绑定到该端口的操作都会失败】
使用一个本地的TCP多口监听来自外部的TCP连接,同时建立多个向外的TCP连接。【Windows、Linux、Unix、MAC等操作系统都支持特殊的TCP套接字参数SO_REUSEADDR,允许应用程序将多个套接字绑定到本地的一个IP:端口号】
3.2 打开P2P打洞的TCP流:假定客户端A希望与B建立TCP连接,依旧有类似UDP打洞的中继服务器。地址获取过程一样。之后的话:
A和B开始等待向外的连接是否成功,检查是否有新连接联入。如果向外的连接由于某种网络错误而失败,如:“连接被重置”或者“节点无法访问”,客户端只需要延迟一小段时间,然后重新发起连接即可。
TCP连接建立起来以后,客户端之间应该开始鉴权操作,确保目前联入的连接就是所希望的连接。如果鉴权失败,客户端将关闭连接,并且继续等待新的连接联入。客户端通常采用“先入为主”的策略,只接受第一个通过鉴权操作的客户端,然后将进入P2P通信过程不再继续等待是否有新的连接联入。
【UDP每个客户端只需要一个套接字就能进行与中继服务器的通信,而TCP必须将多个套接字绑定到同一个本地TCP端口】打洞过程其实和UDP一样。
4 总结
在IP地址极度短缺的今天,NAT几乎已经是无所不在的一项技术了,以至于现在任何一项新技术都不得不考虑和NAT的兼容。作为当下应用最广泛的技术之一,P2P技术也必然要面对NAT这个障碍。
打洞技术看起来是一项近似乎蛮干的技术,却不失为一种有效的技术手段。在集中服务器的帮助下,P2P的双方利用端口预测的技术在NAT网关上打出通道,从而实现NAT穿越,解决了NAT对于P2P的阻隔,为P2P技术在网络中更广泛的推广作出了非常大的贡献。
补充:本文仅仅简单的讲述了打洞的三种方法。需要更加详细的了解UDP、TCP打洞,务必要了解了解这两个协议。
NAT技术基础可参考:网络小白浅谈NAT技术-CSDN博客