New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

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

Open
rainzhaojy opened this Issue Nov 14, 2016 · 7 comments

Comments

Projects
None yet
6 participants
@rainzhaojy
Owner

rainzhaojy commented Nov 14, 2016

实现一个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. 参考文档:

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: "user@example.com"
        }
    ]
}
var pc = new RTCPeerConnection(pcConfig);

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

TURN认证信息的安全性

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: "user@example.com"
        }
    ]
}
var pc = new RTCPeerConnection(pcConfig);

STUN and TURN server

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

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

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

Any other situations not work?

Authenticated Proxy Support in Chrome: bug 439560

@rainzhaojy rainzhaojy added the WebRTC label Nov 14, 2016

@rainzhaojy rainzhaojy changed the title from 怎么样构建高可用的WebRTC应用? - 网络连接篇 to WebRTC: 如何在各种网络环境下都能连接成功? Nov 22, 2016

@rainzhaojy rainzhaojy changed the title from WebRTC: 如何在各种网络环境下都能连接成功? to WebRTC下的网络连接: STUN, TURN, ICE, TCP Dec 9, 2016

@yusangeng

This comment has been minimized.

Show comment
Hide comment
@yusangeng

yusangeng May 24, 2017

也就是说,假设我想开发一个RTC media gateway,用于转发视频流,我就需要在gateway里完整实现TURN server的功能?

yusangeng commented May 24, 2017

也就是说,假设我想开发一个RTC media gateway,用于转发视频流,我就需要在gateway里完整实现TURN server的功能?

@rainzhaojy

This comment has been minimized.

Show comment
Hide comment
@rainzhaojy

rainzhaojy May 25, 2017

Owner

如果要穿透Symmetric NAT,TURN是必须的。TURN server可以是独立的,也可以和media gateway在一起

Owner

rainzhaojy commented May 25, 2017

如果要穿透Symmetric NAT,TURN是必须的。TURN server可以是独立的,也可以和media gateway在一起

@qiuhai1989

This comment has been minimized.

Show comment
Hide comment
@qiuhai1989

qiuhai1989 Sep 25, 2017

怎么测试我搭建的turn服务器 穿透成功了,WebRTC samples Trickle ICE 上测试能返回我的公网ip就说明成功了是吗

qiuhai1989 commented Sep 25, 2017

怎么测试我搭建的turn服务器 穿透成功了,WebRTC samples Trickle ICE 上测试能返回我的公网ip就说明成功了是吗

@xiaomingzheng

This comment has been minimized.

Show comment
Hide comment
@xiaomingzheng

xiaomingzheng Oct 11, 2017

你好 我用tcp做中间做数据传输,但是只能实现局域网,外网就不行了,这跟我用tcp服务端有关系吗

xiaomingzheng commented Oct 11, 2017

你好 我用tcp做中间做数据传输,但是只能实现局域网,外网就不行了,这跟我用tcp服务端有关系吗

@anchengjian

This comment has been minimized.

Show comment
Hide comment
@anchengjian

anchengjian Jan 18, 2018

TURN server的REST API 用法相关,碰到问题的胖友可以看这个问题下的回答😂
https://stackoverflow.com/questions/35766382/coturn-how-to-use-turn-rest-api

anchengjian commented Jan 18, 2018

TURN server的REST API 用法相关,碰到问题的胖友可以看这个问题下的回答😂
https://stackoverflow.com/questions/35766382/coturn-how-to-use-turn-rest-api

@rainzhaojy

This comment has been minimized.

Show comment
Hide comment
@rainzhaojy

rainzhaojy May 24, 2018

Owner

@xiaomingzheng TCP用于媒体传输有时延大、带宽利用不足等问题,因此音视频应用一般使用UDP协议

Owner

rainzhaojy commented May 24, 2018

@xiaomingzheng TCP用于媒体传输有时延大、带宽利用不足等问题,因此音视频应用一般使用UDP协议

@jimmy9065

This comment has been minimized.

Show comment
Hide comment
@jimmy9065

jimmy9065 Jun 30, 2018

有点疑惑想请教一下。
如果我的客户端在一个对称NAT之后, UDP无法回传, 这个时候就要使用TCP对吧?
但是因为客户端在NAT之后, 当Turn server要转发包给这个client的时候,没有办法由Turn server直接发起一个新的TCP请求向其传数据(客户端的NAT不存在对应路由表)。
是否这个场景下 需要使用TCP长连接来实现对称NAT的穿透,谢谢~
不知道我这么说有没有表达清楚哈

jimmy9065 commented Jun 30, 2018

有点疑惑想请教一下。
如果我的客户端在一个对称NAT之后, UDP无法回传, 这个时候就要使用TCP对吧?
但是因为客户端在NAT之后, 当Turn server要转发包给这个client的时候,没有办法由Turn server直接发起一个新的TCP请求向其传数据(客户端的NAT不存在对应路由表)。
是否这个场景下 需要使用TCP长连接来实现对称NAT的穿透,谢谢~
不知道我这么说有没有表达清楚哈

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment