# 端对端连接初体验
# 一、NAT STUN TURN
- NAT
NAT 全称 Network Addresss Translation 即网络地址转换。出现的目的是为了解决 ipv4 不够用的情况同时还能保证内网安全,缺点在于 nat 会降低网速,nat 层数越多网速越慢,同时 nat 如果两边都是对称型或者一边是对称型另一边是端口限制性,就无法进行 p2p 连接了。由于 ipv4 数量不够,不足以让每台设备都拥有一个 ipv4,同时 ipv6 还未普及。所以需要 nat 将内网 ip 转化为公网 ip 中的某个端口。一般一个路由器有一个公网 ip,同时路由器会给连接上路由器的设备一个内网 ip。其中内网 ip 有三类:A 类:10.0.0.0–10.255.255.255,B 类:172.16.0.0–172.31.255.255,c 类:192.168.0.0–192.168.255.255。路由器内的设备在访问外网时,内网 ip 地址经过路由器会映射为路由器外网 ip 地址中的某个端口号,发给某个网站的服务器,此时服务器收到的 ip 地址是路由器的 ip 地址的某个端口而不是内网 ip 地址。路由器收到网站返回的响应,会根据映射表发给相应的内网 ip 的设备。
NAT 有两大类型一个是锥型,另一个是对称型。当路由器内的设备访问某个网站时,路由器会生成一个外网 ip 地址,如果此时在访问另一个网站,路由器生成的外网 ip 地址也是同一个,这就是锥型,如果不同则是对称型。换句话说如果设备在访问不同的网站时,经由 NAT 生成的 ip 地址如果是同一个就是锥型,不同则是对称型。
锥型还分为三种,分别是完全锥型,地址受限锥型,端口受限锥型。如果能通过 NAT 生成的 ip 地址,向该地址发生消息,内网的设备能够直接接收到该消息则是完全锥型,如果要求该内网设备发消息给某个服务器,这个服务器才能通过 NAT 生成的 ip 地址发给该内网设备则是地址受限锥型。如果要求该内网设备发消息给某个服务器,且消息必须由该服务器上收到消息的端口号发送,通过 NAT 生成的 ip 地址发送消息给内网设备则是端口受限锥型。简单的讲如果任何设备都可以通过由 NAT 映射的 ip 地址访问内网设备则是完全锥型,如果需要该内网设备访问过的设备,才能访问内网设备则是地址受限锥型。如果不仅要该内网设备访问过的设备,发送消息时还必须通过接收消息的端口号发送消息,才能访问内网设备则是端口受限锥型。
从宽松程度来看,完全锥型 < 地址受限锥型 < 端口受限锥型 < 对称型 完全锥型最宽松,对称型最严格。
从安全角度来看,完全锥型 < 地址受限锥型 < 端口受限锥型 < 对称型。完全锥型最不安全,对称型最安全。
- STUN
STUN 全称 Simple Traversal of UDP Through NATs,即简单的用 udp 实现 NAT 穿越。如果两个设备拥有公网 ip,那么这两个设备就能互相访问对方也就很轻易的完成 p2p 连接。但现实是,由于 ipv4 紧缺导致设备一般没有公网 ip,用的是由 NAT 分配的内网 ip,而内网 ip 则不能直接访问。这个时候就需要用到 STUN 进行 NAT 穿越,获取设备内网 ip 经由路由器映射下的公网 ip 地址,然后交换双方的公网 ip 地址以实现 p2p 连接。当然并不是所有设备都能 p2p 连接的,如果双方的 NAT 是对称型的或者有一方是对称型另一方是端口受限锥型则无法进行 p2p 连接,这时候就需要使用 TURN
使用 STUN 时,需要在双方设置 STUN 客户端,以及一个双方能访问到的 STUN 服务器 (一般部署到公网上) 。STUN 客户端和 STUN 服务器需要进行至多三次测试,用于判断双方的 NAT 类型,是否能用 UDP 传输以及双方在 NAT 映射下的公网 ip 地址以及端口号。测试如下,图片来自 wiki。
![]()
- TURN
TURN 全称 Traversal Using Relays around NAT,即使用中继实现 NAT 穿越。当通信双方的 NAT 是对称型的或者有一方是对称型另一方是端口受限锥型则无法进行 p2p 连接。这时候就需要使用 TURN 服务器,获取通信双方的音视频相关的数据,再交换。使用 TURN 作中继相比 p2p 连接,延迟要高且耗费流量,如果 TURN 服务器的带宽不够还会降低双方的通信质量,这是无法进行 p2p 连接的无奈之举。
相关资料:
[Ref]: 详解 P2P 技术中的 NAT 穿透原理 (转载) - 简书 (jianshu.com)
[Ref]:P2P 技术详解 (二):P2P 中的 NAT 穿越 (打洞) 方案详解 - 简书 (jianshu.com)
[Ref]: P2P 技术详解 (三):P2P 技术之 STUN、TURN、ICE 详解 (转载) - 简书 (jianshu.com)
# 二、Candidate ICE SDP
candidate 意为候选者,有三种类型分别为 host,srflx,relay。优先度由高到低。
host:如果通信双方处于一个局域网下或者说连接同一个 wife。就可以使用这个类型,优势度最高。具体收集的是,ip 地址和端口。
srflx:如果通信双方不在同一个局域网但可以进行 p2p 连接,则可以走这个类型。优先度仅此于 host
relay:如果双方无法进行 p2p 连接即双方可能是对称型或者一方对称另一方端口限制型的话,则选择这个类型。优先度最低,只有上面两种不行无法进行 p2p 连接才会选择。relay 和 srflx 收集的是经过 NAT 映射后的 ip 地址和端口。
ice:全称 Interactive Connectivity Establishment(交互式连通建立方式)。ice 收集所有通信方式并找到一个最佳的通信方式。比如通过 STUN 找到 srflx 类型的 candidate,通过 TURN 找到 relay 类型的 candidate。优先使用 host,如果不通则尝试 srflx,如果还不通最后使用 relay。
SDP:全称 Session Description Protocol(会话描述协议)。通信双方的设备支持的视频和音频的编解码很大概率是不同,所以需要了解通信双方所支持的音视频编解码,然后从中选择一个双方都有且最好的音视频编解码。sdp 记录了某一方支持的音视频编解码。
# 三、RTCPeerConnection
RTCPeerConnection 是一个构造函数,用于创建 offer 和 answer,设置本地的 sdp 信息以及远端的 sdp 信息。在媒体协商前需将媒体流使用 addstream 或者 addtrack 设置到 RTCPeerConnection 的实例对象中。在设置完媒体流后需要进行媒体协商,发起方需要创建 offer,将 offer 设置为本地的 sdp 信息同时发送给信令服务器。接收方收到信令服务器发送的 offer,将 offer 设置为本地描述,然后创建 answer,将 answer 设置为本地描述同时发送 answer 到信令服务器。接收方收到信令服务器的 answer 将设置远端描述。至此媒体协商完成,在 webrtc 底层会收集 candidate,此时还需要交换双方的 candidate,当交换完成就会使用 ice 找到合适的连接方式。这时候通信双方建立了一条链路。音视频数据可以通过这个链路传输了。当收到远端传来的数据还需要在本地展示出来。需注意的是,需要给 RTCPeerConnection 设置媒体流,在设置本地描述后就会触发 onicecandidate,如果在设置本地描述前没有设置媒体流的话是不会触发 oniecandidate 的。另外接收方将发送方发送的 offer 设置为远端描述后才能创建 answer。整个流程如下。
1 | let local = document.querySelector('#local') |
相关资料:
WebRTC 这么火🔥,前端靓仔,请收下这篇入门教程 - 掘金 (juejin.cn)
# 四、RTCDataChannel
可以使用 RTCDataChannel 可以传输文本,文件以及图片。RTCDataChannel 是由 RTCPeerConnection 实例对象的 createDataChannel () 创建的。
1 | let channel = pc1.createDataChannel('chat') //使用createDataChannel创建通道,第一个参数是通道的名字 |