前言:关于webRTC的基础,具体可参见MDN,本文不做具体阐述。
P2P(Peer-to-Peer)特点:
- 去中心化:P2P网络中没有单一的控制中心或服务器,所有
节点平等、互相连接并共享资源。 - 直接通信:节点之间可以直接交换数据,不需要经过中央服务器转发(需要自行处理信令交换,后面会讲到)。常见的应用包括文件共享、即时通讯、音视频通话等。
- 资源共享:每个节点可以共享自身的计算资源(如带宽、存储、计算能力等)来支持网络中的其他节点。
名词介绍:
为便于读者阅读,特对下文中会出现的名词,做单独解释:
- 对等点:前面说到去中心化,因为没有服务器,即客户端 = 服务器 = 接受者 = 发送者。此处可以理解为具有服务端能力的客户端,而在p2p网络中,会出现2个或者2个以上的对等点,即多对多的关系.
SDP的由来:
我们知道,计算机两个应用程序要通信,必须知道双方的IP+ 端口号。例如,当两个对等点在流式传输视频时,它们需要发送相同的视频分辨率和编解码器。如果你想给你的一个朋友打电话,就必须知道他的电话号码是一个道理。所以我们需要有一个解决方案:让对等点彼此能够交换连接信息。
为了交换这些信息,WebRTC 使用了一种格式化的数据。专业术语为:SDP, 即会话描述协议 (Session Description Protocol)。需要注意的是:SDP 不是一种协议,而是一种数据格式。
以上是 SDP 的示例数据。它描述了会话的基本信息,如媒体类型、编解码器、传输协议等。通过 SDP,对等点能够交换连接信息,从而协商并建立起通信。就像 HTTP 标头一样。对等点需要交换自己的 SDP。然后他们才知道如何相互连接。 SDP对应的字段非常多,计划后面写一遍单独讲解SDP的文章,本期可以参阅SDP 文档,初步了解webRTC的同学,可以不做过多深入研究,用到SDP涉及的字段,查文档即可。
信令交互:
在 WebRTC 中,交换 SDP 的过程称为信令交互(Signaling) 只有交换SDP后,对等点才能建立p2p连接,但是,WebRTC 中对于对等点信令如何交互,并没有规范定义。
翻译过来大白话: 如何让客户端A和客户端B,知道彼此的的SDP信息,这事儿得自己想办法搞定,官方没有底层的协议支持。
由于 WebRTC 规范未定义信令,我们甚至可以通过复制粘贴来交换 SDP。业界大多数都是通过 WebSocket 来做到这一点,如下示意图:
而对于信令服务而言,仅仅是完成来回传递信息,并不关心传递的内容。
信令交互过程中,会涉及:NAT穿透、STUN服务器、ICE交互等,这些概念比较晦涩难懂,我们将通过代码+图文解释的形式,一步步深入了解。 先看看下面的代码:
const urls = [
"stun:stun.services.mozilla.com",
"stun:stunserver2024.stunprotocol.org",
"stun:stun.l.google.com:19302",
];
const connection = new RTCPeerConnection({
iceServers: [{ urls }],
});
上面的代码,我们定义了一个 RTCPeerConnection 对象,配置了多个 STUN 服务器来支持 NAT 穿透,使得两个客户端可以在没有直接 IP地址的情况下建立点对点连接。其实背后做了非常多的事情,首先这里引出了 STUN和 NAT的概念。
关于 NAT(网络地址转换):
在讲NAT穿透和STUN之前,需要先清楚它们诞生的背景和意义。 NAT穿透又被称之为:P2P打洞,它是IPv4 时代的产物,主要是为了解决IPv4 地址数量有限的问题,而在IPv6中,是不需要这么做的。
理想网络情况:
理想中我们希望通过 信令服务交互 SDP后,peerA就直接能和peerB建立连接,发送数据。
实际中的网络(多数情况):
在IPv4 时代,由于IP数量的不足,通常会由运营商为每个用户分配一个外网IP,而在这个外网下,其他设备想上网,就只能通过路由器为每个设备分配一个内网IP.
而路由器中有一个关键协议:网络地址转换(NAT),其中有一个重要的功能:将内网 IP 地址转换为其所拥有的公网 IP 地址,从而使多个设备能够通过同一个公网 IP 地址共享互联网连接,但同时NAT还有一层防护作用,会阻止外网直接访问内网设备,在提供安全的同时,同时也对p2p连接造成了一定的困扰(后面会讲)
NAT基本原理,如下图所示:
配图解释: 主机A、B、C在在同一个局域网下,被路由器分配了各自的私有IP,当内部设备发送请求到外网服务器时,会先经过路由器,NAT映射表会记录当前主机在内网与外网的映射关系(ip和端口),当服务器响应后,会将响应的数据包返回给路由器,再通过NAT找到对应私有设备。
以上就是NAT的基本工作原理,其中NAT除了有地址共享能力以外,还具备对外隐藏内网IP、和防火墙的能力。
不同的NAT类型,对p2p的支持力度也不一样,不过不管是哪种类型,它们都有一个共同特性:只有在内网设备主动向外网设备发起连接后,NAT才会允许响应。
| NAT类型 | 外部设备能否主动与内网设备通信 | 穿透难度 | 安全性 | 共同点 |
|---|---|---|---|---|
| 完全锥形NAT | 只要暴露了外网IP+端口就能直接通信 | 最低 | 低 | 只有在内网设备主动向外网设备发起连接后,NAT才会允许响应。 |
| 受限锥形NAT | 必须先向外发起连接 | 较低 | 中 | |
| 端口受限锥形NA | 必须先向外发起连接且端口一致 | 高 | 中 | |
| 对称NAT | 外部无法主动与内网设备通信 | 最高 | 高 |
网上有很多对于NAT类型的介绍,多半是模凌两可,但是NAT的类型又决定了p2p 选用哪种连接,所以我们有必要掌握。这里通过自己的个人理解做了总结:
完全锥形NAT: 如果内网设备A有主动向外网设备B,发送过一次请求后(TCP/UDP),只要其他外网设备(可以是非B)知道内网设备的A+IP和端口,就能主动向A发送请求,而无需经过 A的同意。
受限锥形NAT: 类似完全锥形NAT,区别在于只有与 A 发起请求时 目标 IP 地址相同 的外网设备 B,才能通过 NAT 发送数据到内网设备 A。有点感觉像“我见过你,你才能进”。
端口受限锥形NA: 在受限锥形NAT基础上,又加强了端口校验。
我们可以将 端口受限锥形 NAT 类比为一个 "门禁系统" ,它不仅需要你有“门票”(即正确的 IP 地址),还要求你必须用**特定的通道(端口)**进入。
想象你进入一个大楼,在楼下的大厅有一个门禁系统。你持有一张有效的门票(公网 IP)可以进入大厅,但你只能通过指定的门(端口)进入。这就像是 端口受限锥形 NAT。 你只能通过最初给你“门票”的那个门(即 IP 和端口组合)进入。如果你从其他门(其他端口)进入,门禁系统会拒绝你。
对称型 NAT: 特点:对每个外部主机或端口的会话都会映射为不同的端口
上文只是便于初学者理解,如果想深入NAT可以参阅官方文档
在清楚NAT的几种类型和基本原理后,我们就不难理解,为什么peerA和peerB在不同网段下无法通信了,总结原因主要有2点:
- 不知道彼此的外网IP和端口
- 即使知道了IP和端口,受限于NAT类型的拦截
关于STUN:
而STUN 服务的意义是帮助内网设备获取其在公网中的映射 IP 地址和端口, 客户端A和客户端B在2个不同的网段,通过STUN服务器,可以获取到自己的公网IP和端口,然后通过 websocket 将自己的公网IP和端口发送给对方,对方就可以通过这个公网IP和端口连接到对方了(前提是NAT能够穿越成功的情况下)
STUN 只是用于获取设备的外网IP和端口,所消耗的资源极少,所以一般有开源的公共服务提供。例如我们上面的代码, 声明了多个STUN服务,如果一个STUN服务器无法响应,RTCPeerConnection的底层会尝试其他服务器连接。这种方式有效避免单点故障,提高NAT的成功率。
在学习p2p过程中,我们暂不需要过多关注STUN服务器的具体实现,初学者了解基本原理即可。
# PeerConnection 连接流程:
参考链接: medium.com/@avocadi/p2…