WebRTC下的网络连接: STUN, TURN, ICE, TCP

实现一个WebRTC demo是比较容易的, 但如果要做一个webrtc产品, 则需要在任何网络环境下都能够建立网络连接.

Background: NAT

多数联网设备都位于局域网中, 并位于防火墙后面, 设备本身只有一个内网的私有IP, 在与外部通信时, 会经过1个或多个NAT路由器, 最终得到一个最外端的一个外部IP, 然后与远端目标机器通讯. 这一网络结构对于web应用, c/s型应用等来说不是问题, 但对于VoIP/P2P等应用就是一个问题了. 通信双方并不知道自己或对方的outermost的外网IP:Port, 如何建立直连呢?

这时就需要NAT穿透, 目前WebRTC采用的是ICE框架 (ICE+STUN+TURN), ICE也适用于非WebRTC应用, 这是目前业界用于穿透NAT的标准方案.

ICE使用了STUN, TURN等技术, 并扩展了SDP. ICE会同时尝试找出两个机器可行的连接方式, 并选择一个最高效的连接方式来穿透NAT. 参考文档:

  • RFC 5389 - STUN
  • RFC 5766 - TURN
  • RFC 5245 - ICE, updated by RFC 6336
  • RFC 6544 - TCP ICE
  • trickle ICE
  • SIP usage for trickle ICE
  • Tunneling WebRTC over TCP (and why it matters) - 介绍了STUN/TURN的作用
  • What kind of TURN server is being used?? - 提到了17.7%经过TURN中转

Level 1: STUN

搭建一个简单的WebRTC应用, 然后在同一个局域网里的两个机器上分别打开页面, 可以建立WebRTC连接并进行video chat.

然后你想和局域网外的一个朋友video chat, 你会发现你们是无法连接成功的. 你不知道自己的外网地址, 你的朋友也不知道, 基于内网地址你们是无法建立UDP连接的.

这时你需要STUN server, 你可以自己部署一个STUN server, 或者找现成的STUN server, 并在你的WebRTC里配置了这个STUN server, 然后你们就可以video chat了:

var pcConfig = {
    iceServers: [
        {urls: "stun:stun.example.com:19302"}
    ]
}
var pc = new RTCPeerConnection(pcConfig);

STUN URI的格式为: stunURI = (stun|stuns):$host[:$port], stun的默认端口为3478, stuns的默认端口为5349.

STUN回答了"What is my IP?"这个问题, 并不参与接下来的media data中转.

Level 2: TURN

STUN能够解决大多数的NAT穿越问题, 但是不能穿越Symmetric NAT.

如果你和你的朋友在各自的corporate network下, 那么你们就很可能在Symmetric NAT下, 只是配置STUN server你们仍然是无法连接成功的.

这时你需要TURN server, TURN server一般同时也是STUN server. TURN会中转media data, 因此TURN server的通讯压力远大于STUN server.

你可以自己在公网里部署一个TURN server, 也可以使用别人部署的TURN server, 由于TURN server会做media relay, 出于安全和性能原因, 一般应该部署自己的TURN server.

在WebRTC里配置STUN和TURN:

var pcConfig = {
    iceServers: [
        {urls: "stun:stun.example.com:19302"},
        {
            urls: "turn:turn.example.com", 
            credential: "webrtcdemo", 
            username: "[email protected]"
        }
    ]
}
var pc = new RTCPeerConnection(pcConfig);

TURN URI的格式如下: turnURI = (turn|turns):$host[:$port][?transport=(udp|tcp)], turn默认端口和stun一样,turn为3478, turns为5349.

TURN要求使用long-term credential认证方式, 因此username/credential对于TURN是必须的, 在webrtc里使用TURN server时, 这就表示username/credential需要写在javascript代码里, 这就有安全问题了, 一般的做法是每次使用TURN server之前从server拿到一个临时的username/password.

有一个推荐做法是让TURN server提供REST API来返回一个token: A REST API For Access To TURN Services

Level 3: TCP

配置了TURN和STUN后, 你的WebRTC应该能在大多数情况下工作了, 但还是有可能无法建立连接.

有些公司的firewall可能会关闭UDP连接, 这时就需要TCP连接了, 所以你的WebRTC也需要在TCP上工作.

有两种方式支持TCP连接:STUN over TCP与TURN over TCP.

1) STUN over TCP

当remote candidates里包含TCP相关candidates时,browser也会尝试TCP连接,如果UDP不通而TCP可以连通,browser就是使用TCP连接。

2) TURN over TCP

TURN一般使用UDP, 但TURN也支持TCP, 在WebRTC里通过TCP to TURN server来实现TCP连接, 需要配置TURN并且transport设为tcp:

var pcConfig = {
    iceServers: [
        {urls: "stun:stun.example.com:19302"},
        {
            urls: "turn:turn.example.com?transport=tcp", 
            credential: "webrtcdemo", 
            username: "[email protected]"
        }
    ]
}
var pc = new RTCPeerConnection(pcConfig);

STUN and TURN server

出于安全和性能原因, 你可能需要部署自己的TURN server, 有一些TURN open source projects, 譬如: rfc5766-turn-server, coturn. STUN/TURN server必须部署在公网上.

如果你的产品是有media server (SFU or MCU)的, 那么你也可以把STUN/TURN的功能做在media server里.

STUN/TURN server部署好后,可以通过WebRTC samples Trickle ICE测试你的Server。

你可能感兴趣的:(WebRTC下的网络连接: STUN, TURN, ICE, TCP)