浅谈 WebRTC P2P 打洞 (上)

837 阅读8分钟

前言:关于webRTC的基础,具体可参见MDN,本文不做具体阐述。

P2P(Peer-to-Peer)特点:

  1. 去中心化:P2P网络中没有单一的控制中心或服务器,所有节点平等、互相连接并共享资源。
  2. 直接通信:节点之间可以直接交换数据,不需要经过中央服务器转发(需要自行处理信令交换,后面会讲到)。常见的应用包括文件共享、即时通讯、音视频通话等。
  3. 资源共享:每个节点可以共享自身的计算资源(如带宽、存储、计算能力等)来支持网络中的其他节点。

名词介绍:

为便于读者阅读,特对下文中会出现的名词,做单独解释:

  • 对等点:前面说到去中心化,因为没有服务器,即客户端 = 服务器 = 接受者 = 发送者。此处可以理解为具有服务端能力的客户端,而在p2p网络中,会出现2个或者2个以上的对等点,即多对多的关系.

SDP的由来:

我们知道,计算机两个应用程序要通信,必须知道双方的IP+ 端口号。例如,当两个对等点在流式传输视频时,它们需要发送相同的视频分辨率和编解码器。如果你想给你的一个朋友打电话,就必须知道他的电话号码是一个道理。所以我们需要有一个解决方案:让对等点彼此能够交换连接信息。

为了交换这些信息,WebRTC 使用了一种格式化的数据。专业术语为:SDP, 即会话描述协议 (Session Description Protocol)。需要注意的是:SDP 不是一种协议,而是一种数据格式

image.png

以上是 SDP 的示例数据。它描述了会话的基本信息,如媒体类型、编解码器、传输协议等。通过 SDP,对等点能够交换连接信息,从而协商并建立起通信。就像 HTTP 标头一样。对等点需要交换自己的 SDP。然后他们才知道如何相互连接。 SDP对应的字段非常多,计划后面写一遍单独讲解SDP的文章,本期可以参阅SDP 文档,初步了解webRTC的同学,可以不做过多深入研究,用到SDP涉及的字段,查文档即可。

信令交互:

在 WebRTC 中,交换 SDP 的过程称为信令交互(Signaling) 只有交换SDP后,对等点才能建立p2p连接,但是,WebRTC 中对于对等点信令如何交互,并没有规范定义。

翻译过来大白话: 如何让客户端A和客户端B,知道彼此的的SDP信息,这事儿得自己想办法搞定,官方没有底层的协议支持。

由于 WebRTC 规范未定义信令,我们甚至可以通过复制粘贴来交换 SDP。业界大多数都是通过 WebSocket 来做到这一点,如下示意图: chrome-capture-2024-11-5.png

而对于信令服务而言,仅仅是完成来回传递信息,并不关心传递的内容。

信令交互过程中,会涉及: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地址的情况下建立点对点连接。其实背后做了非常多的事情,首先这里引出了 STUNNAT的概念。

关于 NAT(网络地址转换):

在讲NAT穿透和STUN之前,需要先清楚它们诞生的背景和意义。 NAT穿透又被称之为:P2P打洞,它是IPv4 时代的产物,主要是为了解决IPv4 地址数量有限的问题,而在IPv6中,是不需要这么做的。

理想网络情况:

image.png

理想中我们希望通过 信令服务交互 SDP后,peerA就直接能和peerB建立连接,发送数据。

实际中的网络(多数情况):

image.png

在IPv4 时代,由于IP数量的不足,通常会由运营商为每个用户分配一个外网IP,而在这个外网下,其他设备想上网,就只能通过路由器为每个设备分配一个内网IP. 而路由器中有一个关键协议:网络地址转换(NAT),其中有一个重要的功能:将内网 IP 地址转换为其所拥有的公网 IP 地址,从而使多个设备能够通过同一个公网 IP 地址共享互联网连接,但同时NAT还有一层防护作用,会阻止外网直接访问内网设备,在提供安全的同时,同时也对p2p连接造成了一定的困扰(后面会讲)

NAT基本原理,如下图所示:

image.png

配图解释: 主机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的同意。

image.png

受限锥形NAT: 类似完全锥形NAT,区别在于只有与 A 发起请求时 目标 IP 地址相同 的外网设备 B,才能通过 NAT 发送数据到内网设备 A。有点感觉像“我见过你,你才能进”。

image.png

端口受限锥形NA: 在受限锥形NAT基础上,又加强了端口校验。

我们可以将 端口受限锥形 NAT 类比为一个 "门禁系统" ,它不仅需要你有“门票”(即正确的 IP 地址),还要求你必须用**特定的通道(端口)**进入。

想象你进入一个大楼,在楼下的大厅有一个门禁系统。你持有一张有效的门票(公网 IP)可以进入大厅,但你只能通过指定的门(端口)进入。这就像是 端口受限锥形 NAT。 你只能通过最初给你“门票”的那个门(即 IP 和端口组合)进入。如果你从其他门(其他端口)进入,门禁系统会拒绝你。

image.png

对称型 NAT: 特点:对每个外部主机或端口的会话都会映射为不同的端口

image.png

上文只是便于初学者理解,如果想深入NAT可以参阅官方文档

在清楚NAT的几种类型和基本原理后,我们就不难理解,为什么peerA和peerB在不同网段下无法通信了,总结原因主要有2点:

  1. 不知道彼此的外网IP和端口
  2. 即使知道了IP和端口,受限于NAT类型的拦截

关于STUN:

而STUN 服务的意义是帮助内网设备获取其在公网中的映射 IP 地址和端口, 客户端A和客户端B在2个不同的网段,通过STUN服务器,可以获取到自己的公网IP和端口,然后通过 websocket 将自己的公网IP和端口发送给对方,对方就可以通过这个公网IP和端口连接到对方了(前提是NAT能够穿越成功的情况下)

STUN 只是用于获取设备的外网IP和端口,所消耗的资源极少,所以一般有开源的公共服务提供。例如我们上面的代码, 声明了多个STUN服务,如果一个STUN服务器无法响应,RTCPeerConnection的底层会尝试其他服务器连接。这种方式有效避免单点故障,提高NAT的成功率。

在学习p2p过程中,我们暂不需要过多关注STUN服务器的具体实现,初学者了解基本原理即可。

# PeerConnection 连接流程:

下载.png

参考链接: medium.com/@avocadi/p2…

www.cnblogs.com/hls-code/p/…

WebRTC 学习指南

如何搭建自己的STUN服务器