从 P2P 说起
传统的 P2P 下载中,一般有两个角色:Tracker 和 Peer。Peer 一般是每个用户,他们之间互相传输数据(既下载又上传),Tracker 则负责收集所有在下载同一资源的用户和他们的进度,让他们互相之间能够 “被发现”,从而能够在复杂的网络环境中建立起连接。

实现一个 P2P 模式的网络需要做两件事:第一件事是让 Peer 们可以互相发现对方,第二件事是让 Peer 们可以互相建立起连接。第一件事很好办,只要能有一个公共的服务器,不论是 HTTP 协议,还是其他的任何协议(比如 WebSocket)。第二件事需要做 NAT 穿透,在 Web 上就比较难办了。在这之前,大家都使用 Flash 来实现。今天,可以使用 WebRTC 来实现。
WebRTC
很多人对 WebRTC 的印象都停留在 “在线音视频” 的印象中。其实 WebRTC 全称是 Web Real-Time Communications,即 “网页即时通信”。在今天,它也可以用来传输音频、视频以外的信息,例如图片、二进制文件等。
WebRTC 更底层使用到了ICE。一个最基本的 WebRTC 结构一般是由几部分组成:
- STUN 服务器
- TURN 服务器(可选)
- Peers(客户端)
STUN 服务器的工作职责很简单,它用来获取客户端的公开网络信息。例如自己的公网 IP、是否能够被直接访问到等。TURN 则是一个 “增强版” 的 STUN。除了 STUN 本身的识别功能外,它还带有转发功能:如果不能被直接连接,它还同时承担着中继的责任。
另外,WebRTC 还有一个 “信令层”,它并不限制你用什么方式实现,你可以使用 WebSocket,可以使用 XHR…… 它唯一的作用,就是用来告诉其它客户端自己准备好了。
因此,如果两台机器(假设是 A、B)之间需要建立连接,它们一般有这样的流程:(下面就不涉及发送流什么的了,只作为介绍基本流程)
- A、B 分别初始化一个 WebRTC。
- A 创建了一个 offer,它是一个SDP,然后通过信令服务器发送给 B。
- B 取得 A 的 offer,然后创建了一个 answer,通过信令服务器发送给 A。
- A 取得 B 的 answer,并通过 STUN 和 native 能力拿到了自己的网络信息,生成一个 Candidate,通过信令服务器发送给 B,表示自己已经准备好了。
- B 取得 A 的 Candidate,并同样创建了一个自己的 Candidate,发送给 A。
- A 取得 B 的 Candidate,开始正式建立连接(P2P 打洞,如果不通的话就走转发)。
整体流程图如下:

其中涉及到几个东西:
- Candidate:包含了基本的网络信息,例如自身局域网的 IP、公网 IP、TURN 服务器 IP、STUN 服务器 IP 等。
- SDP:包含了一些音视频的相关信息。
在连接正式建立完成之后,就可以互相发送数据了。
P2P CDN
现在市面上有很多利用上述技术进行 CDN 分发的案例了。例如阿里云有PCDN,又拍云有PrismCDN,腾讯云有X-P2P。非常典型的用户就是各大直播、视频平台了。使用 P2P CDN,相对于传统的 CDN 来说成本更低,并且在高并发的情况下体验会更好。以直播分发为例,传统的 CDN 一般是:
- 将直播流裁剪成很多个片段(长度视平台不同而不同,一般是 5-10s),分发至 CDN 节点
- 实时更新一个 m3u8 播放列表,将每个片段的 URL 放入其中。
- 客户端从 CDN 下载片段,拼合到一起进行播放。
使用了 P2P CDN 之后,大体上仍然是这样,但其将片段拆的更细,将一部分内容使用 P2P 的形式分发。例如,原本的直播流有一个 10s 的片段,其大小为 5MB。P2P CDN 将其拆成了 10 个更小的片段,每个片段只有 512KB,我们将其编码为片段 1 - 片段 10。
- 用户 A、B、C 在同一网络或相近的网络,观看这个直播。
- 用户 A 从 CDN 下载了片段 1-10。
- 用户 B 也需要这些片段,于是从 CDN 下载了 2-10,从用户 A 处获得了片段 1。
- 用户 C 也需要这些,于是从 CDN 下载了 2-9,然后从用户 A 处获得了片段 1、从用户 B 处获得了片段 10。
- ……
这样下来,可以发现,实际上用户 A 的体验会有一定降低(因为被拆成更多次的请求,导致整体稍慢一些),但是用户 B、用户 C,因为其从更近的地方下载到了部分片段,整体体验反而更佳。对于 CDN 厂商来说,流量成本也有所下降。
以斗鱼为例
举一个实际的例子:斗鱼。斗鱼大规模使用到了 P2P CDN。初步观察其使用到了很多家服务,包括腾讯云、阿里云、聚网优速,以及自建服务(使用了网宿 CDN)。
- 在观看直播时,其会建立一个类似于
trk-xxx-xxx-xxx-xxx.douyucdn.cn的 WebSocket 长连接作为信令。 - 通过其获取到其他客户端,并与其交换所需要的数据,建立起连接。
有一位群友碰到了上行网络被斗鱼占满的情况。其实理论上不会出现:斗鱼只会将众多片段的一部分进行上传。但如果真的出现了,可以通过屏蔽 P2P 连接的方式,使其降级为传统 CDN。屏蔽方式很简单:屏蔽信令服务或者屏蔽 STUN 服务。其中,腾讯云的 STUN 服务是 IP 地址形式,不太好屏蔽。但是它们的信令不约而同都采用了 WebSocket,反而比较好屏蔽。
举个例子,使用 uBlock 将它们加入黑名单,即可屏蔽掉对应的 WebSocket 连接:
/trk-([0-9\-]+)\.douyucdn\.cn/
||signal.qvb.qcloud.com
||.trk.galaxyclouds.cn