跳到内容
Tony

P2P 技术及其应用

我们知道,常规的视频直播通常是通过 rtsp 或者 hls 协议直播的,这种直播的特点是所有的流量都需要从中心服务器发出,经过 CDN(Content Delivery Network),下发到各个终端。

技术 , 计算机网络 2 分钟阅读

我们知道,常规的视频直播通常是通过 rtsp 或者 hls 协议直播的,这种直播的特点是所有的流量都需要从中心服务器发出,经过 CDN(Content Delivery Network),下发到各个终端。

这种直播适合大量人群观看,但是服务器的带宽成本也很高。

为了节约成本,基于 P2P 技术的音视频直播应运而生,且已广泛应用于在线聊天(webRTC),网络摄像机等领域。

那么什么是 P2P 技术呢?这种技术又如何应用于视频直播呢?

对等式网络(peer-to-peer, 简称 P2P),又称点对点技术,是无中心服务器、依靠用户群(peers)交换信息的互联网体系,它的作用在于,减低以往网路传输中的节点,以降低资料遗失的风险。与有中心服务器的中央网络系统不同,对等网络的每个用户端既是一个节点,也有服务器的功能,任何一个节点无法直接找到其他节点,必须依靠其户群进行信息交流。

听上去和 Git 有着异曲同工之妙。

所以,只要知道了对方的 IP 地址,就能直接连接了吗?

在没有配置 NPTv6(Network Prefix Translation for IPv6) 的 IPv6 网络下,如果知道了对方的 IPv6 地址,是可以直接连接的,IPv6 地址通常是全球唯一的,因此每个设备可以直接在 Internet 上具有全局可达的 IPv6 地址。

但是,在 IPv4 网络环境下,无法直接连接,因为存在 NAT(Network Address Translation)

NAT 技术是为了解决 IPv4 地址短缺问题而提出的。IPv4 地址是 32 位二进制数,可以表示的地址数量是 2 的 32 次方,也就是 4294967296 个。然而,其中一部分地址被保留用于特殊用途,比如私有地址、广播地址、多播地址等,因此实际可用的IPv4地址数量要比这个数字少一些。此外,IPv4 地址的分配也不是均匀的,有些地区和组织分配到的地址比其他地区和组织多,这也导致了 IPv4 地址的短缺问题。

NAT 技术通过将内部网络的私有 IP 地址转换为公共 IP 地址,使得多个内部主机可以共享一个公共 IP 地址访问互联网,从而解决了 IPv4 地址短缺的问题。此外,NAT 技术还可以提高网络安全性,因为它可以隐藏内部网络的 IP 地址,使得外部网络无法直接访问内部网络。

NAT 有三种类型:

  • 静态 NAT,静态一对一的地址转换。需要手动配置映射关系,若网路拓扑改变,则需更新配置,管理与维护难度大。通常用于企业内部网络
内网 IP外网 IP
192.168.1.55219.152.168.222
192.168.1.59219.152.168.223
192.168.1.155219.152.168.224
  • 动态 NAT(Pooled NAT),动态地址池转换。内部网络的私有 IP 地址转换时从一个地址池中选取公有 IP 地址,对应关系有租期限制 内网 IP 和外网 IP 也是一对一的映射关系,和静态 NAT 的区别就是映射关系会动态改变

  • NAPT(Network Address Port Translation,端口地址转换):是一种特殊的动态 NAT,它不仅将内部主机的IP地址映射到一个公共 IP 地址,还将内部主机的端口号映射到公共 IP 地址的不同端口号上,以实现多个内部主机共享一个公共 IP 地址访问外部网络。

内网 IP外网 IP
192.168.1.55:5555219.152.168.222:9200
192.168.1.59:80219.152.168.223:9201
192.168.1.155:4456219.152.168.224:9202

家用路由器一般使用 NAPT,拥有复杂网络的大型公司内网则会使用 Static NATDynamic NAT,本文讨论的 NAT 是第三种 NAT。

第三种 NAPT 又可以分为锥型和对称型 NAT (Symmetric NAT)。锥型 NAT 又可以细分为 Full Cone NAT, Address Restricted Cone NAT, Port Restricted Cone NAT,每一种 NAT 对于外部请求的响应方式有所不同。

  • Full Cone NAT,完全锥型

一旦内部地址(iAddr:iPort)映射到外部地址(eAddr:ePort),所有发自 iAddr:iPort 的数据包都经由 eAddr:ePort 向外发送。任意外部主机都可以通过发送数据包给 eAddr:ePort 给 NAT设备内部的 iAddr:iPort 主机。

  • Address Restricted Cone NAT,地址受限型

在完全锥型 NAT 的基础上限制了 IP 地址。只有和内部地址有过通信的外部地址,才能发送消息

  • Port Restricted Cone NAT,端口受限型

在地址受限型 NAT 的基础上限制了端口,特定外部地址的特定端口,才能发送消息

  • Symmetric NAT,对称型

可以用如下代码在 Chrome 浏览器里检测当前网络的 NAPT 的类型:

// parseCandidate from https://github.com/fippo/sdp
function parseCandidate(line) {
var parts;
// Parse both variants.
if (line.indexOf('a=candidate:') === 0) {
parts = line.substring(12).split(' ');
} else {
parts = line.substring(10).split(' ');
}
var candidate = {
foundation: parts[0],
component: parts[1],
protocol: parts[2].toLowerCase(),
priority: parseInt(parts[3], 10),
ip: parts[4],
port: parseInt(parts[5], 10),
// skip parts[6] == 'typ'
type: parts[7]
};
for (var i = 8; i < parts.length; i += 2) {
switch (parts[i]) {
case 'raddr':
candidate.relatedAddress = parts[i + 1];
break;
case 'rport':
candidate.relatedPort = parseInt(parts[i + 1], 10);
break;
case 'tcptype':
candidate.tcpType = parts[i + 1];
break;
default: // Unknown extensions are silently ignored.
break;
}
}
return candidate;
};
var candidates = {};
var pc = new RTCPeerConnection({iceServers: [
{urls: 'stun:stun1.l.google.com:19302'},
{urls: 'stun:stun2.l.google.com:19302'}
]});
pc.createDataChannel("foo");
pc.onicecandidate = function(e) {
if (e.candidate && e.candidate.candidate.indexOf('srflx') !== -1) {
var cand = parseCandidate(e.candidate.candidate);
if (!candidates[cand.relatedPort]) candidates[cand.relatedPort] = [];
candidates[cand.relatedPort].push(cand.port);
} else if (!e.candidate) {
if (Object.keys(candidates).length === 1) {
var ports = candidates[Object.keys(candidates)[0]];
console.log(ports.length === 1 ? 'normal nat' : 'symmetric nat');
}
}
};
pc.createOffer()
.then(offer => pc.setLocalDescription(offer))

IP 数据包通过 NAT 设备(如路由器)时,NAT 会重写来源 IP 地址和目的地 IP 地址,从而实现同一内网中不同主机共用公网 IP 地址的功能。

NATNAT示意图

如上图所示,192.168.100.3 的主机向 209.131.36.158 的 80 端口发送 HTTP 请求,经过 NAT 时,NAT 根据其保存的映射表,找到该主机对应的外网 IP 地址和端口,即 145.12.131.7:6282,然后替换 IP 数据包的 Source 字段为该地址,并发送给 www.yahoo.com。雅虎服务器发送 response 的时候,Dest 即为 145.12.131.7:6282,然后 NAT 转发给 192.168.100.33855 端口。

但是,如果某个外网主机想主动访问 192.168.100.3,显然不能到达内网的 192.168.100.3 主机,即使该主机知道了我们的公网 IP,它在主动建立连接时,NAT 发现并没有该地址和局域网内地址的映射,该数据包就会被 NAT 设备丢弃。

NAT 技术使用非常广泛,但也存在一些缺点:

  1. NAT 设备需要对收发的数据包重新编辑修改(IP 地址转换,重新计算校验),此操作会降低网络数据的传输速度
  2. NAT 设备端口老化问题,会导致连接中的设备异常断开,因为 NAT 设备需要维护端口映射表,而硬件资源有限,所以有些 NAT 设备会定时断开部分连接
  3. 部分网络协议无法通过 NAT 设备,让两台设备直接连接变得困难,因此,才出现了 NAT 打洞技术

NAT 打洞是一种技术,用于在两个位于不同私有网络中的设备之间建立直接通信。NAT 打洞通常使用 UDP 协议,所以也称为 UDP 打洞。 下面是 UDP 打洞的基本流程:

  1. 设备 A 和设备 B 都位于不同的私有网络中,它们都无法直接通信。
  2. 设备 A 向设备 B 发送一个 UDP 数据包,这个数据包包含了设备 A 的 IP 地址和端口号。
  3. 设备 B 收到这个数据包后,会记录下设备 A 的 IP 地址和端口号,并向设备A发送一个 UDP 数据包,这个数据包包含了设备 B 的 IP 地址和端口号。
  4. 设备 A 收到这个数据包后,会记录下设备 B 的 IP 地址和端口号。
  5. 现在,设备 A 和设备 B 都知道了对方的 IP 地址和端口号,它们可以直接通过这些信息进行 UDP 通信了。
Server S
18.181.0.31:5678
|
|
+----------------------+----------------------+
| |
NAT A NAT B
155.99.25.11:62000 138.76.29.7:31000
| |
| |
Client A Client B
192.168.0.100:1234 10.1.1.3:1234

那么问题来了,设备 A 向设备 B 发送一个 UDP 数据包,那么设备 A 如何知道设备 B 的 IP 地址和端口号呢?这就需要中介服务器了。 这个中介服务器可以是一个公网服务器或者是一个 STUN 服务器

STUN(Session Traversal Utilities for NAT)是一种用于穿越网络地址转换(NAT)的协议,它通常用于 P2P 通信中的打洞操作。P2P 打洞旨在允许两个设备在 NAT 后进行直接通信,而不需要通过中间服务器。

STUN

Google 提供的 STUN 服务器,可以使用 webRTC 的 demo 测试下。

NAT 有不同的类型,不是所有 NAT 都支持 NAT 打洞,如前面提到的 Symmetric NAT 则无法打洞。UDP 打洞的成功率约为 60%,当打洞失败时,则使用基于 TURN 服务器的中继模式。

Traversal Using Relays around NAT (TURN)旨在通过打开与 TURN 服务器的连接并通过该服务器中继所有信息来绕过对称 NAT 限制。您将创建与 TURN 服务器的连接,并告诉所有对等方将数据包发送到服务器,然后将其转发给您。这显然会带来一些开销,因此只有在没有其他选择的情况下才使用它。

TURN

经过 TURN 服务器中转的模式,通常称为 relay

参考资料