webrtc之建立连接

442 阅读8分钟

前面两篇介绍了 媒体协商SDP,知道了 媒体协商 就是双方互相传递各自的 SDP,即媒体信息。也知道了协商的过程,主动方发起 offer SDP,应答方回应 answer SDP,之后就算是交换成功,能处上了。但怎么互相交换 SDP的呢?有哪些方式可以做到呢?下面就来说道说道。

首先,A 和 B 要建立连接,显然只有两种情况:

  1. A 和 B 是同一个网络环境,直接连接
  2. A 和 B 不是同一个网络环境,间接连接

现在分析第一种情况,在同一个网络环境下。这时就是通过内网IP地址和端口相互连接,不需要经过任何中继服务器,直接实现真正的端到端连接。这种肯定是最优且最理想的情况了,两点之间直线最短了。它具有的优点如下:

  1. 低延迟 - 数据直接在终端之间传输,不经过中继服务器,延迟最小。
  2. 高带宽 - 终端之间的带宽不受中继服务器带宽限制。
  3. 安全性高 - 数据不经过第三方服务器,更加安全。
  4. 成本低 - 不需要使用昂贵的中继服务器资源。

总的来说就是十项全能,能内网就内网。但是,大多数终端设备位于NAT(网络地址转换)或防火墙之后,很难直接获取公网IP和端口,因此内网直连通常很难实现。当然,如果说你想在小范围内使用的话,内网还是可以的。

再看第二种情况,不在同一个网络环境下。这时候也有两种方式进行连接:

  • 想尽办法拿到公网IP地址和端口,然后再尝试 直连 (STUN协议)
  • 实在没办法拿到公网IP地址和端口,使用 中间商的方式 建立连接 (TURN协议)

是不是总感觉这俩协议长得一样,没错,我也有这种感觉。

先看第一种 STUN协议,一句话解释就是 允许位于NAT 和防火墙后的客户端找出自己的公网IP地址和端口,达到NAT穿透的目的。说人话就是 拿到客户端的公网IP地址和端口。有了公网ip地址和端口,那就方便了,可以直接进行连接了。

你可能第一次听 STUN协议,不知道是个啥。没关系,也不需要知道的那么多。知道能通过它拿到公网IP地址和端口就行。

这里简要介绍下如何通过 STUN协议 拿到公网IP地址和端口,它的大致过程如下:

  1. 部署一个 STUN 服务器,比如 CoTURN
  2. 客户端向公网上的STUN服务器发送Binding Request请求报文
  3. STUN服务器收到请求后,记录下该请求的源IP地址和端口号,即客户端在NAT网关上映射的公网IP地址和端口号
  4. TUN服务器将记录的公网IP地址和端口号放入Binding Response响应报文中的XOR-MAPPED-ADDRESS属性中,并将响应报文发送回客户端
  5. 客户端收到响应报文后,从XOR-MAPPED-ADDRESS属性中解析出自己的公网IP地址和端口号

其实就是部署个服务器,然后发送一条请求,再分析请求返回的结果就能拿到公网IP地址和端口号。就这么简单明了。

至于 NAT 是什么,下一节会详细介绍的。

再来看看 TURN协议,也是加个服务器,只是这个服务器是中继服务器,用来中转消息的。相当于是两个客户端通过中介的方式来进行连接,一切消息都由中介传递。

当 STUN 协议无法建立直接的端到端连接时,TURN 协议会使用中继服务器来转发数据。客户端首先向 TURN 服务器申请一个中继地址和端口,然后将数据发送到这个中继地址,TURN 服务器会将数据转发给对端。

那么现在来看看两个协议的优缺点:

协议优点缺点
STUN 协议1.无需中继服务器,直接建立端到端的 UDP 连接,延迟低,带宽占用小;2.简单易实现,只需要客户端支持 STUN 协议即可如果双方客户端都在严格的防火墙后面,STUN 可能无法穿透
TURN 协议可以穿透严格的防火墙,即使双方客户端都在防火墙后面也能建立连接1.需要中继服务器转发数据,延迟会增加,带宽占用也会增加;2.中继服务器的性能和带宽成为瓶颈

相对来说,STUN协议 的优先级是大于 TURN协议,毕竟 STUN协议 更方便好用一点。但是, TURN协议 可以适应最极端的情况,无论如何都可以让两个客户端进行连接,缺点就是你的中继服务器决定了你的上限。

ICE协议

现在已经知道了A和B建立连接的三种方式:直连(也叫p2p)、间接直连(STUN协议)和中介连接(TURN协议)。这三种方式的优先级肯定是 直连 > 间接直连 > 中介连接。这点应该没有任何疑问了,那个好用优先考虑那个。

我们能想到这三种连接方式,也知道它们的优劣点。那么webrtc肯定也是考虑到了。所以,webrtc中有一个 ICE协议,这个协议就是专门用来筛选出合适的连接方式的。

先不去管这个协议的具体内容,太复杂了,下面会简单介绍几个重点的。这里暂时只考虑它的用处。webrtc会根据这个 ICE协议,最先尝试 直连 方式,如果直连能成功,则后面不再考虑。然后直连不成,那就再尝试使用 间接直连 方式,如果能成功,就用这种方式。如果这个还没成功,那就使用 中介连接 方式,这个一定能成功(不成功说明中继服务器有问题)。

说白了,这个 ICE协议就是一个不断尝试的过程,依据优先级来尝试,一旦成功就不再考虑后面的。这个协议确保了A和B之间一定能连上,且在效率和性能上尽量保证最大化。这就是 ICE协议 的作用。

OK,到这里已经知道 ICE协议 的作用了。再来看看webrtc是如何使用这个协议的。

在webrtc中有一个onicecandidate方法,如下:

  pc.onicecandidate = async (e) => {
    if (e.candidate) {
      const params = {
        userId,
        targetUserId,
        candidate: e.candidate,
      };
      linkSocket.value.emit("candidate", params);
    } else {
      console.log("在此次协商中,没有收集到新的 ICE 候选");
    }
  };

这个方法会返回所有收集到的candidate,也叫做候选者。没错,这些候选者就是上面那三种连接方式,只是具体化了,用实际的对象来表示了,而且还出现了变种。

ICE Candidate 主要包括以下内容:

  1. 候选地址收集: ICE 客户端会收集各种类型的候选地址,包括本地主机地址、服务器反射地址、对等方反射地址和中继地址。这些候选地址用于建立连接和进行连通性测试。

  2. 候选地址类型:

    • Host: 本地主机地址,即设备在同一局域网内的直接连接。
    • Server Reflexive (srflx) : 通过 STUN 服务器获取的公共 IP 地址,用于建立点对点连接。
    • Peer Reflexive (prflx) : 对等方自己的 NAT 映射地址。
    • Relay: 通过 TURN 服务器中继传输数据的地址。
  3. 候选地址属性:

    • Component ID: 传输媒体的类型,如 RTP 和 RTCP。
    • Priority: 候选地址的优先级,用于确定最佳的传输路径。
    • Type: 候选地址的类型,如 Host、Server Reflexive、Peer Reflexive 和 Relay。
    • Protocol: 传输协议,通常为 UDP。
    • IP: 候选地址的 IP 地址。
    • Port: 候选地址的端口号。
    • Rel-Addr 和 Rel-Port: 如果候选地址是中继类型,这两个属性表示中继服务器分配的相对地址和端口。
    • Raddr 和 Rport: 如果候选地址是服务器反射类型,这两个属性表示 STUN 服务器看到的客户端的地址和端口。

看那个地址类型,有没有很眼熟,是不是就能直接对应上前面介绍的几种连接方式了。第三种prflx则是 STUN协议的变种(在我看来)。

然后webrtc在收集完所有候选者信息之后,依据优先级来逐个尝试连接。至于怎么连接的,那是它自己的事情,管不着。

这是我电脑上的效果

image.png

是不是有种 so easy 的感觉。没错,确实是很简单。一看就懂,有手就行。

小节

本文介绍了webrtc实现端到端建立连接的基本过程。在webrtc中,使用优先级的方式去建立连接,直连最先,间接直连靠后,中介连接兜底。这样的话,最大程度上保证了能连上,且连的又好又快。

这里也简单介绍了 STUN协议TURN协议 ,知道了它们的作用和优缺点。在使用时,心里也有点底子了。

总的来说,直连最好了,但局限也最大。使用最多的还是 间接直连。那么要使用 间接直连,则需要了解一下 NAT,下一小节则专门来介绍下 NAT,冲冲冲!

项目地址

前端:gitee.com/yoboom/webr…

后端:gitee.com/yoboom/webr…

项目功能如下:

image.png

感兴趣的可以直接去体验一下,欢迎star和提pr