《Getting Started with WebRTC》第二章 WebRTC技术介绍
本章作WebRTC的技术介绍,主要讲下面的概念:
. 怎样建立P2P的通信
. 有效的信令选项
. 关键API的关系
这些动作通常由Webserver和/或信令server提供。
这个协作能够同意两个或多个WebRTC设备或端找到彼此。交换通信的细节。
协商定义了他们怎样通信的会话,
最后建立它们之间的直播P2P媒体流。
这里仅仅做最简单的流程:两个浏览器使用WebRTC建立一个最简单的视频通话。
以下是这个流程的总结:
. 连接用户
. 启动信令
. 寻找候选者
. 协商媒体会话
. 启动 RTCPeerConnection流
最简单的方式让两个用户訪问同个一网页,这个页面标识了每一个浏览器的訪问后。
然后通过两使用同样的信令server。如WebSocket API,来连接彼此。
这样的类型的Web页面,通常都是给须要通信的浏览器分配一个唯一的标识符,
你能够觉得这个标识符是一个房间号或会话ID。
当第一个用户訪问网页时。会生成一个唯一的URL。这个唯一的URL就是ID号,
然后第一个用户将这个ID发送给第二个用户。
当这两个用户都打开了这个URL时,第一步就完毕了。
在这里。信令消息能够是不论什么形式的通信。它用来帮助两浏览器建立并控制它们的WebRTC通信。
WebRTC标准并没有严格定义怎样实现它。
实际上这是一个长处,由于它将这一部分放开。能带来创新和发展。
当然,这相同也是一个挑战。由于这样的不确定会导致开发人员不好区分谁是RTC通信的新用户。
前面的apprtc DEMO演示了使用XHR和Google AppEngine Channel API的组合使用:
(https://developers.google.com/appengine/docs/python/channel/overview).
非常easy的,这也能够是不论什么其他XHR轮循。server发送事件:
(http://www.html5rocks.com/en/tutorials/eventsource/basics/),
WebSockets
(http://www.html5rocks.com/en/tutorials/websockets/basics/),
的组合使用。
这个过程通常叫做“寻找候选者”,
当这一步结束后,浏览器能被映射到可直接訪问的网络接口和port。
每一个浏览器都可能是位于一个路由器之后,
这个路由器可能使用了Network address Translation(NAT)来连接本地网络之后再连到互联网,
也可能它是被防火墙做了限制。比方堵塞了某些port和输入连接。
找到一种方式去连接这些类型的路由器经常使用的方法是NAT Traversl(NAT穿越):
(http://en.wikipedia.org/wiki/NAT_traversal),
而且它对于建立WebRTC通信极为重要。
实现NAT穿越的一种经常使用方式是使用一个Session Traversal Utilities for NAT (STUN)server,
(http://en.wikipedia.org/wiki/Session_Traversal_Utilities_for_NAT),
它能非常easy地帮助识别公网上的连接并返回实用的信息。
有非常多人都提供了公开的STUNserver,apprtc演示样例使用了Google提供的。
假设STUNserver不能找到一种方式从公网上和你的浏览器建立连接,
那你就不得不回退到使用中转媒介,如Traversal Using Relay NAT (TURN) server
(http://en.wikipedia.org/wiki/Traversal_Using_Relay_NAT).
来解决问题。
这种做的副作用是把你回退到了非端到端的结构中,
但在某些情况下。你可能在很特殊的受限的私有网络中,这可能是你自己的选择。
对于WebRTC来说,这整个过程通常绑定到单个的
InteractiveConnectivity Establishment (ICE) 框架中
(http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment),
它会按要求从STUNserver回退到TURNserver。
NOTE:
NAT穿越(NAT traversal)涉及TCP/IP网络中的一个常见问题,
即在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题.
概述:
会遇到这个问题的一般是那些client网络交互应用程序的开发者,尤其是在对等网络和VoIP领域中。
IPsec VPN客户普遍使用NAT-T来达到使ESP包通过NAT的目的。
虽然有很多穿越NAT的技术。但没有一项是完美的。这是由于NAT的行为是非标准化的。
这些技术中的大多数都要求有一个公共server,
并且这个server 使用的是一个众所周知的、从全球不论什么地方都能訪问得到的IP地址。
一些方法仅在建立连接时须要使用这个server,
而其他的方法则通过这个server中继全部的数据——这就引入了带宽开销的问题.
方法:
1. NAT/ALG 方式
普通NAT是通过改动UDP或TCP报文头部地址信息实现地址的转换。
但对于VOIP应用,在TCP/UDP净载中也需带地址信息。
ALG方式是指在私网中的VOIP终端在净载中填写的是其私网地址,
此地址信息在通过NAT时被改动为NAT上对外的地址。
语音和视频协议(H323、SIP、MGCP/H248)的识别和对NAT/Firewall的控制,
同一时候每添加一种新的应用都将须要对 NAT/Firewall进行升级。
在安全要求上还须要作一些折衷,
由于ALG 不能识别加密后的报文内容,所以必须保证报文採用明文传送。
这使得报文在公网中传送时有非常大的安全隐患。
NAT/ALG是支持VOIP NAT穿透的一种最简单的方式。
但因为网络实际情况是已部署了大量的不支持此种特性的NAT/FW设备。
因此,实际应用中,非常难採用这样的方式。
2. MIDCOM 方式
与NAT/ALG不同的是,MIDCOM的基本框架是採用可信的第三方(MIDCOM Agent)对Middlebox (NAT/FW)进行控制。
VOIP协议的识别不由Middlebox完毕,而是由外部的MIDCOM Agent完毕,
因此VOIP使用的协议对Middlebox是透明的 .
因为识别应用协议的功能从Middlebox移到外部的MIDCOM Agent上。
依据MIDCOM 的构,在不须要更改Middlebox基本特性的基础上。
通过对MIDCOM Agent的升级就能够支持很多其它的新业务,这是相对NAT/ALG方式的一个非常大的优势。
在VOIP实际应用中,Middlebox功能可驻留在NAT/Firewall,
通过软交换设备(即MIDCOM Agent)对IP语音和视频协议(H323、SIP、MGCP/H248)
的识别和对NAT/Firewall的控制,来完毕VOIP应用穿越 NAT/Firewall .
在安全性上。MIDCOM方式可支持控制报文的加密。可支持媒体流的加密,因此安全性比較高。
假设在软交换设备上实现对SIP/H323/MGCP/H248协议的识别。
就仅仅需在软交换和NAT/FW设备上添加MIDCOM协议就可以,
并且以后新的应用业务识别随着软交换的支持而支持,
此方案是一种比較有前途的解决方式。但要求现有的NAT/FW设备需升级支持MIDCOM协议,
从这一点上来说,对已大量部署的NAT/FW设备来说,也是非常困难的,同NAT/ALG方式有同样的问题。
3. STUN 方式
解决穿透NAT问题的还有一思路是,私网中的VOIP终端通过某种机制预先得到出口NAT上的对外地址。
然后在净载中所填写的地址信息直接填写出口NAT上的对外地址,而不是私网内终端的私有IP地址。
这样净载中的内容在经过NAT时就无需被改动了,仅仅需按普通NAT流程转换报文头的IP地址就可以。
净载中的 IP地址信息和报文头地址信息是一致的。
STUN协议就是基于此思路来解决应用层地址的转换问题。
STUN的全称是Simple Traversal of UDP Through Network Address Translators,
即UDP对NAT的简单穿越方式。
应用程序(即STUN CLIENT)向NAT外的STUN SERVER通过UDP发送请求STUN 消息。
STUN SERVER收到请求消息,产生响应消息,响应消息中携带请求消息的源port,
即STUN CLIENT在NAT上相应的外部port。然后响应消息通过NAT发送给STUN CLIENT,
STUN CLIENT通过响应消息体中的内容得知其NAT上的外部地址。
并将其填入以后呼叫协议的UDP负载中。告知对端,本端的RTP接收地址和port号为NAT 外部的地址和port号。
因为通过STUN协议已在NAT上预先建立媒体流的NAT映射表项。故媒体流可顺利穿越NAT.
STUN协议最大的长处是无需现有NAT/FW设备做不论什么修改。
因为实际应用中。已有大量的NAT/FW,而且这些NAT/FW并不支持VoIP的应用,
假设用MIDCOM或NAT/ALG方式来解决此问题。须要替换现有的NAT/FW,这是不太easy的。
而採用STUN方式无需修改NAT/FW,这是其最大优势,同一时候STUN方式可在多个NAT串联的网络环境中使用,
但MIDCOM方式则无法实现对多级NAT的有效控制。
STUN的局限性在于须要VOIP终端支持STUN CLIENT的功能,
同一时候STUN并不适合支持TCP连接的穿越,因此不支持H323.
另外STUN方式不支持对防火墙的穿越。不支持对称NAT (Symmetric NAT)类型
(在安全性要求较高的企业网中。出口NAT一般是这样的类型)穿越。
4. TURN方式
TURN方式解决NAT问题的思路与STUN相似,
也是私网中的VOIP终端通过某种机制预先得公网上的服务地址(STUN方式得到的地址为出口NAT上外部地址,
TURN方式得到地址为TURN Server上的公网地址)。
然后在报文净载中所要求的地址信息就直接填写该公网地址。
TURN的全称为Traversal Using Relay NAT,即通过Relay方式穿越NAT.
TURN应用模型通过分配TURN Server的地址和port作为私网中VOIP终端对外的接受地址和port,
即私网终端发出的报文都要经过TURN Server进行Relay转发,这样的方式除了具有STUN方式的长处外,
还攻克了STUN应用无法穿透对称NAT(Symmetric NAT)以及类似的Firewall设备的缺陷,
同一时候TURN支持基于TCP的应用,如H323协议。此外TURN Server控制分配地址和port,
能分配RTP/RTCP地址对(RTCPport号为RTPport号加1)作为私网终端用户的接受地址。
避免了STUN方式中出口NAT对RTP/RTCP地址port号的随意分配,
使得client无法收到对端发来的RTCP报文(对端发RTCP报文时,目的port号缺省按RTPport号加 1发送)。
TURN的局限性在于须要VOIP终端支持TURN Client,这一点同STUN一样对网络终端有要求。
此外,全部报文都必须经过TURN Server转发。增大了包的延迟和丢包的可能性。
5. ICE方式
应该说ICE是眼下在NAT穿透中最经常使用的方式。
ICE(交互式连接建立)- Interactive Connectivity Establishment 是一种综合性的NAT穿越的技术。
交互式连接建立是由IETF的MMUSIC工作组开发出来的一种framework,
可整合各种NAT 穿透技术。如STUN、TURN(Traversal Using Relay NAT)、
RSIP(Realm Specific IP,特定域IP)等。
该framework能够让SIP的client利用各种NAT穿透方式打穿远程的防火墙。
包含codec, 分辨率。码率等。
通常能够使用基于 offer/answer的模型来进行协商,
比如能够使用Session Description Protocol (SDP)
(http://en.wikipedia.org/wiki/Session_Description_Protocol).
它已经被JavaScript Session Establishment Protocol (JSEP)定义。
很多其它的信息能够訪问:
http://tools.ietf.org/html/draft-ietf-rtcweb-jsep-00)
by the IETF
两者能够直接通过它们的P2P连接,也能够是通过不论什么中继媒介网关。
在这个阶段,浏览器为了共享通信以控制WebRTC的通信,能够连续地使用同一个信令server方案。
它们也能够使用一个特殊WebRTC数据通道来直接通信做到这一点。
仅仅须要非常easy地使用:
var connection = new WebSocket(url);
就能创建一个新的连接,
然后,构造函数就会创建你自己的函数来处理收到的消息和错误。
发送消息也非常easy,使用:
connection.send(message)
方法就可以。
使用WebSocket的最大优点是它的消息是真正双向的,高速且轻量的。
这意味着WebSocket APIserver能够在不论什么想要的时候直接发送消息到你的浏览器,
而且你相同能即时收到它们。
它们没有延时或像在XHR轮循或长轮循模型那样的常量的网络堵塞,
WebSocket APIserver能够使用前面提到的唯一的房间或会话标识符,
来计算得到哪个WebSocket APIclient的消息应当被中转。
在这样的方式中,单个WebSocket APIserver能够支持大量的client。
而且由于建立网络连接的发生次数很少,因此消息本身是倾向于小的,因此要求的server资源也很少。
在绝大多数的主流编程语言中有非常多可用的WebSocket API库,
而且由于Node.js是基于JavsScript的。所以它变成了实现这样的类型的功能很流行的选择。
像socket.io(http://socket.io/)库都提供非常多easy使用的演示样例程序。
最简单的模式是使用XHR API来发送消息,并通过定期轮循服务来收集不论什么新的消息。
这样的方式能够非常easy实现,且不须要不论什么额外的工具。
然而,它有一些缺点。
它有一个基于每一个轮循周期的必定延时,
并且它非常浪费带宽,由于即时没有消息准备发送或接收,轮循也是周期反复的。
可是。假设你要想要一个好用的老式的方案,那它就是的。
有一种基于轮循的略微精细点的方法,叫做长轮循。
在这样的模型中,通过使用HTTP 1.1的keep-alive机制。
假设server没有不论什么消息,那么网络连接就一直保持。
当server有新的信息时,它就立即发送它到连接以完毕请求。
在这样的情况下。降低了轮循的网络开销。
相比于更现代的WebSocket模式,它还是过时和低效的方法。
Server-Send Events是还有一个方式。
你能够使用:
var source = new EventSource(url)
来建立到server的连接。构造函数然后加入对source对象的监听器,
用来处理由server发来的消息。
这样的方式同意server直接发送消息,且你能即时收到。
但你使用的还是一个分立的通道,如XHR,来发送你的消息到server。,
这意味你被强制去管理和同步两个独立的管道。
这样的组合模式提供了一个实用的方案,它被用在了一些WebRTC演示样例应用中。
可是它还不是真正的双向管道。
getUserMedia()方法是初始化訪问本地输入设备的主要方法。
每个MediaStream对象能够包括多个不同的MediaStreamTrack对象--它们每个都表示一个不同的输入媒体,
如源自不同输入源的视频或音频。
每一个MediaStreamTrack能够包括多个通道(如左右声道)。
通道是MediaStream API定义的最小单元。
MediaStream对象能够以两种主要方式输出。
首先,它们能够被用来渲染输出给 MediaElemet, 如 <video>或<audio>元素。
第二,它们能够被用来发送到一个RTCPeerConnection--它能够将媒体流发送到远端。
每一个MediaStreamTrack能够使用一系列状态来表示,
由MediaSourceStates对象的states()方法的返回值描写叙述。
每一个MediaStreamTrack相同提供了一系列能力。能够通过capabilities()方法来訪问。
在最上层。MediaStream对象能够触发一系列的事件,比如addtrack, removetrack,或ended.
而下层的MediaStreamTrack也能够触发一系列的事件,如started, mute, unmute, voerconstrainted 和 ended.
能够使用:
var peerconnection = RTCPeerConnection(configuration);
来创新一个RTCPeerConnection对象。
configuration变量包括了至少一个基本的 iceServers的名字--它是一个URL对象数组,
包括了STUN,TURNserver的信息。用来在候选查找。
peerconnection对象在随后的每一个client。依赖于它是呼叫者还是被呼叫者,在使用时会略微有些不同。
2.11.1 呼叫者流程
以下是peerconnection对象创建后,呼叫者的流程总结:
. 注冊 onicecandidate 句柄
. 注冊 onaddstream 句柄
. 注冊 message 句柄
. 使用 getUserMedia 訪问本地摄像头
. JSEP offer/answer 处理
1. 注冊 onicecandidate 句柄
首先,须要注冊一个onicecandidate句柄来发送不论什么ICE候选给其他端,
一旦收到后。就会使用当中一个信令通道。
2. 注冊 onaddstream 句柄
然后。一旦收到了远端的视频流,就须要注冊一个 onaddstream句柄来显示这个视频流。
3. 注冊 message 句柄
你的信令通常也须要有一个句柄被注冊,用来响应从其他端收到的消息。
假设消息中包括有 RTCIceCandidate对象,
那么应该使用addIceCandidate()方法将它加入到 peerconnection对象中。
假设消息中还包括有 RTCSessionDescription对象 ,
那么应该使用 setRemoteDescription()方法将它加入到 peerconnection对象中。
4. 使用 getUserMedia 訪问本地摄像头
然后,你能够使用 getUserMedia()来设置你的本地媒体流和本地页面的显示,
相同的。也须要使用 addStream()方法将它加入到 peerconnection对象中。
5. JSEP offer/answer 处理
如今,你须要使用 createOffer()方法来准备好開始协商,
并注冊一个回调句柄--它由RTCSessionDescription对象收到。
这个回调句柄应当使用 setLocalDescription()将RTCSessionDescription加入到你的peerconnection对象。
最后,你要通过信令通道将这个RTCSessionDescription发送到远端。
2.11.2 被呼叫者流程
The following is a summary of the callee's flow, which is very similar in a lot of
ways to the caller's flow, except that it responds to the offer with an answer:
以下是被呼叫都的流程总结,除了提供一个应答响应外。它和呼叫者的流程非常相似:
. 注冊 onicecandidate 句柄
. 注冊 onaddstream 句柄
. 注冊 消息句柄
. 使用 getUserMedia 訪问本地摄像头
. JSEP offer/answer 处理
1. 注冊 onicecandidate 句柄
Just like the caller, you start by registering an onicecandidate handler that sends
any ICE candidates to the other peer as they are received, using one of the signaling
channels described previously.
2. 注冊 onaddstream 句柄
Then, like the caller, you register an onaddstream handler that displays the video
stream once it is received from the remote peer.
3. 注冊 消息句柄
Like the caller, your signaling channel should also have a handler registered
that responds to messages received from the other peer. If the message contains
an RTCIceCandidate object, it should add those to the peerconnection
object using the addIceCandidate() method. And if the message contains an
RTCSessionDescription object, it should add those to the peerconnection
object using the setRemoteDescription() method.
4. 使用 getUserMedia 訪问本地摄像头
Then, like the caller, you can utilize getUserMedia() to set up your local media
stream and display that on your local page, and also add it to the peerconnection
object using the addStream() method.
5. JSEP offer/answer 处理
Here you differ from the caller and you play your part in the negotiation by
passing remoteDescription to the createAnswer() method and registering a
callback handler that receives an RTCSessionDescription object. This callback
handler should then add this RTCSessionDescription to your peerconnection
object using setLocalDescription(). And then finally, it should also send this
RTCSessionDescription to the remote peer through your signaling channel. It is
also important to note that this callee flow is all initiated after the offer is received
from the caller.
它显示了隐藏在RTCPeerConnection API下的复杂结构。
图1 WebRTC架构图 来自 www.WebRTC.org
它还能够使用 DataChannelAPI传输二进制数据流。
这个API最准确的定义应该是 WebRTC DataChannel API,
它由:
var datachannel = peerconnection.createDataChannel(label);
创建。
它非常灵活。也非常强大,和WebSocket API的send()方法相似。
本章作WebRTC的技术介绍,主要讲下面的概念:
. 怎样建立P2P的通信
. 有效的信令选项
. 关键API的关系
2.1 设置通信
虽然WebRTC通信的基础是P2P的。 但设置这个通信的初始步骤是要求一些协作的。这些动作通常由Webserver和/或信令server提供。
这个协作能够同意两个或多个WebRTC设备或端找到彼此。交换通信的细节。
协商定义了他们怎样通信的会话,
最后建立它们之间的直播P2P媒体流。
2.2 一般流程
应用场景事实上是非常多的,从简单的页面DEMO到多方会议。这里仅仅做最简单的流程:两个浏览器使用WebRTC建立一个最简单的视频通话。
以下是这个流程的总结:
. 连接用户
. 启动信令
. 寻找候选者
. 协商媒体会话
. 启动 RTCPeerConnection流
2.3 连接用户
通信处理的第一步是通过某种方式连接两个用户。最简单的方式让两个用户訪问同个一网页,这个页面标识了每一个浏览器的訪问后。
然后通过两使用同样的信令server。如WebSocket API,来连接彼此。
这样的类型的Web页面,通常都是给须要通信的浏览器分配一个唯一的标识符,
你能够觉得这个标识符是一个房间号或会话ID。
当第一个用户訪问网页时。会生成一个唯一的URL。这个唯一的URL就是ID号,
然后第一个用户将这个ID发送给第二个用户。
当这两个用户都打开了这个URL时,第一步就完毕了。
2.4 启动信令
如今两个用户共享了同一个标识符。它们能够通过交换信令消息来协商建立他们的WebRTC连接。在这里。信令消息能够是不论什么形式的通信。它用来帮助两浏览器建立并控制它们的WebRTC通信。
WebRTC标准并没有严格定义怎样实现它。
实际上这是一个长处,由于它将这一部分放开。能带来创新和发展。
当然,这相同也是一个挑战。由于这样的不确定会导致开发人员不好区分谁是RTC通信的新用户。
前面的apprtc DEMO演示了使用XHR和Google AppEngine Channel API的组合使用:
(https://developers.google.com/appengine/docs/python/channel/overview).
非常easy的,这也能够是不论什么其他XHR轮循。server发送事件:
(http://www.html5rocks.com/en/tutorials/eventsource/basics/),
WebSockets
(http://www.html5rocks.com/en/tutorials/websockets/basics/),
的组合使用。
2.5 寻找候选者
接下来的步骤是在两个浏览器间交换信息。包含它们的网络,及它们怎样取得联系。这个过程通常叫做“寻找候选者”,
当这一步结束后,浏览器能被映射到可直接訪问的网络接口和port。
每一个浏览器都可能是位于一个路由器之后,
这个路由器可能使用了Network address Translation(NAT)来连接本地网络之后再连到互联网,
也可能它是被防火墙做了限制。比方堵塞了某些port和输入连接。
找到一种方式去连接这些类型的路由器经常使用的方法是NAT Traversl(NAT穿越):
(http://en.wikipedia.org/wiki/NAT_traversal),
而且它对于建立WebRTC通信极为重要。
实现NAT穿越的一种经常使用方式是使用一个Session Traversal Utilities for NAT (STUN)server,
(http://en.wikipedia.org/wiki/Session_Traversal_Utilities_for_NAT),
它能非常easy地帮助识别公网上的连接并返回实用的信息。
有非常多人都提供了公开的STUNserver,apprtc演示样例使用了Google提供的。
假设STUNserver不能找到一种方式从公网上和你的浏览器建立连接,
那你就不得不回退到使用中转媒介,如Traversal Using Relay NAT (TURN) server
(http://en.wikipedia.org/wiki/Traversal_Using_Relay_NAT).
来解决问题。
这种做的副作用是把你回退到了非端到端的结构中,
但在某些情况下。你可能在很特殊的受限的私有网络中,这可能是你自己的选择。
对于WebRTC来说,这整个过程通常绑定到单个的
InteractiveConnectivity Establishment (ICE) 框架中
(http://en.wikipedia.org/wiki/Interactive_Connectivity_Establishment),
它会按要求从STUNserver回退到TURNserver。
NOTE:
NAT穿越(NAT traversal)涉及TCP/IP网络中的一个常见问题,
即在处于使用了NAT设备的私有TCP/IP网络中的主机之间建立连接的问题.
概述:
会遇到这个问题的一般是那些client网络交互应用程序的开发者,尤其是在对等网络和VoIP领域中。
IPsec VPN客户普遍使用NAT-T来达到使ESP包通过NAT的目的。
虽然有很多穿越NAT的技术。但没有一项是完美的。这是由于NAT的行为是非标准化的。
这些技术中的大多数都要求有一个公共server,
并且这个server 使用的是一个众所周知的、从全球不论什么地方都能訪问得到的IP地址。
一些方法仅在建立连接时须要使用这个server,
而其他的方法则通过这个server中继全部的数据——这就引入了带宽开销的问题.
方法:
1. NAT/ALG 方式
普通NAT是通过改动UDP或TCP报文头部地址信息实现地址的转换。
但对于VOIP应用,在TCP/UDP净载中也需带地址信息。
ALG方式是指在私网中的VOIP终端在净载中填写的是其私网地址,
此地址信息在通过NAT时被改动为NAT上对外的地址。
语音和视频协议(H323、SIP、MGCP/H248)的识别和对NAT/Firewall的控制,
同一时候每添加一种新的应用都将须要对 NAT/Firewall进行升级。
在安全要求上还须要作一些折衷,
由于ALG 不能识别加密后的报文内容,所以必须保证报文採用明文传送。
这使得报文在公网中传送时有非常大的安全隐患。
NAT/ALG是支持VOIP NAT穿透的一种最简单的方式。
但因为网络实际情况是已部署了大量的不支持此种特性的NAT/FW设备。
因此,实际应用中,非常难採用这样的方式。
2. MIDCOM 方式
与NAT/ALG不同的是,MIDCOM的基本框架是採用可信的第三方(MIDCOM Agent)对Middlebox (NAT/FW)进行控制。
VOIP协议的识别不由Middlebox完毕,而是由外部的MIDCOM Agent完毕,
因此VOIP使用的协议对Middlebox是透明的 .
因为识别应用协议的功能从Middlebox移到外部的MIDCOM Agent上。
依据MIDCOM 的构,在不须要更改Middlebox基本特性的基础上。
通过对MIDCOM Agent的升级就能够支持很多其它的新业务,这是相对NAT/ALG方式的一个非常大的优势。
在VOIP实际应用中,Middlebox功能可驻留在NAT/Firewall,
通过软交换设备(即MIDCOM Agent)对IP语音和视频协议(H323、SIP、MGCP/H248)
的识别和对NAT/Firewall的控制,来完毕VOIP应用穿越 NAT/Firewall .
在安全性上。MIDCOM方式可支持控制报文的加密。可支持媒体流的加密,因此安全性比較高。
假设在软交换设备上实现对SIP/H323/MGCP/H248协议的识别。
就仅仅需在软交换和NAT/FW设备上添加MIDCOM协议就可以,
并且以后新的应用业务识别随着软交换的支持而支持,
此方案是一种比較有前途的解决方式。但要求现有的NAT/FW设备需升级支持MIDCOM协议,
从这一点上来说,对已大量部署的NAT/FW设备来说,也是非常困难的,同NAT/ALG方式有同样的问题。
3. STUN 方式
解决穿透NAT问题的还有一思路是,私网中的VOIP终端通过某种机制预先得到出口NAT上的对外地址。
然后在净载中所填写的地址信息直接填写出口NAT上的对外地址,而不是私网内终端的私有IP地址。
这样净载中的内容在经过NAT时就无需被改动了,仅仅需按普通NAT流程转换报文头的IP地址就可以。
净载中的 IP地址信息和报文头地址信息是一致的。
STUN协议就是基于此思路来解决应用层地址的转换问题。
STUN的全称是Simple Traversal of UDP Through Network Address Translators,
即UDP对NAT的简单穿越方式。
应用程序(即STUN CLIENT)向NAT外的STUN SERVER通过UDP发送请求STUN 消息。
STUN SERVER收到请求消息,产生响应消息,响应消息中携带请求消息的源port,
即STUN CLIENT在NAT上相应的外部port。然后响应消息通过NAT发送给STUN CLIENT,
STUN CLIENT通过响应消息体中的内容得知其NAT上的外部地址。
并将其填入以后呼叫协议的UDP负载中。告知对端,本端的RTP接收地址和port号为NAT 外部的地址和port号。
因为通过STUN协议已在NAT上预先建立媒体流的NAT映射表项。故媒体流可顺利穿越NAT.
STUN协议最大的长处是无需现有NAT/FW设备做不论什么修改。
因为实际应用中。已有大量的NAT/FW,而且这些NAT/FW并不支持VoIP的应用,
假设用MIDCOM或NAT/ALG方式来解决此问题。须要替换现有的NAT/FW,这是不太easy的。
而採用STUN方式无需修改NAT/FW,这是其最大优势,同一时候STUN方式可在多个NAT串联的网络环境中使用,
但MIDCOM方式则无法实现对多级NAT的有效控制。
STUN的局限性在于须要VOIP终端支持STUN CLIENT的功能,
同一时候STUN并不适合支持TCP连接的穿越,因此不支持H323.
另外STUN方式不支持对防火墙的穿越。不支持对称NAT (Symmetric NAT)类型
(在安全性要求较高的企业网中。出口NAT一般是这样的类型)穿越。
4. TURN方式
TURN方式解决NAT问题的思路与STUN相似,
也是私网中的VOIP终端通过某种机制预先得公网上的服务地址(STUN方式得到的地址为出口NAT上外部地址,
TURN方式得到地址为TURN Server上的公网地址)。
然后在报文净载中所要求的地址信息就直接填写该公网地址。
TURN的全称为Traversal Using Relay NAT,即通过Relay方式穿越NAT.
TURN应用模型通过分配TURN Server的地址和port作为私网中VOIP终端对外的接受地址和port,
即私网终端发出的报文都要经过TURN Server进行Relay转发,这样的方式除了具有STUN方式的长处外,
还攻克了STUN应用无法穿透对称NAT(Symmetric NAT)以及类似的Firewall设备的缺陷,
同一时候TURN支持基于TCP的应用,如H323协议。此外TURN Server控制分配地址和port,
能分配RTP/RTCP地址对(RTCPport号为RTPport号加1)作为私网终端用户的接受地址。
避免了STUN方式中出口NAT对RTP/RTCP地址port号的随意分配,
使得client无法收到对端发来的RTCP报文(对端发RTCP报文时,目的port号缺省按RTPport号加 1发送)。
TURN的局限性在于须要VOIP终端支持TURN Client,这一点同STUN一样对网络终端有要求。
此外,全部报文都必须经过TURN Server转发。增大了包的延迟和丢包的可能性。
5. ICE方式
应该说ICE是眼下在NAT穿透中最经常使用的方式。
ICE(交互式连接建立)- Interactive Connectivity Establishment 是一种综合性的NAT穿越的技术。
交互式连接建立是由IETF的MMUSIC工作组开发出来的一种framework,
可整合各种NAT 穿透技术。如STUN、TURN(Traversal Using Relay NAT)、
RSIP(Realm Specific IP,特定域IP)等。
该framework能够让SIP的client利用各种NAT穿透方式打穿远程的防火墙。
2.6 协商媒体会话
如今两个浏览器怎样进行彼此对话了,它们必须都允许他们将交互的媒体(如音频和视频)的类型和格式。包含codec, 分辨率。码率等。
通常能够使用基于 offer/answer的模型来进行协商,
比如能够使用Session Description Protocol (SDP)
(http://en.wikipedia.org/wiki/Session_Description_Protocol).
它已经被JavaScript Session Establishment Protocol (JSEP)定义。
很多其它的信息能够訪问:
http://tools.ietf.org/html/draft-ietf-rtcweb-jsep-00)
by the IETF
2.7 启动 RTCPeerConnection流
当前面的都完毕后,最后浏览器就能启动彼此的流媒体交互。两者能够直接通过它们的P2P连接,也能够是通过不论什么中继媒介网关。
在这个阶段,浏览器为了共享通信以控制WebRTC的通信,能够连续地使用同一个信令server方案。
它们也能够使用一个特殊WebRTC数据通道来直接通信做到这一点。
2.8 使用WebSockets
WebSocket API能让Web开发人员非常方便地在他们的Web应用开发出双向通信功能。仅仅须要非常easy地使用:
var connection = new WebSocket(url);
就能创建一个新的连接,
然后,构造函数就会创建你自己的函数来处理收到的消息和错误。
发送消息也非常easy,使用:
connection.send(message)
方法就可以。
使用WebSocket的最大优点是它的消息是真正双向的,高速且轻量的。
这意味着WebSocket APIserver能够在不论什么想要的时候直接发送消息到你的浏览器,
而且你相同能即时收到它们。
它们没有延时或像在XHR轮循或长轮循模型那样的常量的网络堵塞,
WebSocket APIserver能够使用前面提到的唯一的房间或会话标识符,
来计算得到哪个WebSocket APIclient的消息应当被中转。
在这样的方式中,单个WebSocket APIserver能够支持大量的client。
而且由于建立网络连接的发生次数很少,因此消息本身是倾向于小的,因此要求的server资源也很少。
在绝大多数的主流编程语言中有非常多可用的WebSocket API库,
而且由于Node.js是基于JavsScript的。所以它变成了实现这样的类型的功能很流行的选择。
像socket.io(http://socket.io/)库都提供非常多easy使用的演示样例程序。
2.9 其他信令选项
不论什么通过一种同意浏览器通过server来发送和接收消息的方法都能够用作WebRTC信令。最简单的模式是使用XHR API来发送消息,并通过定期轮循服务来收集不论什么新的消息。
这样的方式能够非常easy实现,且不须要不论什么额外的工具。
然而,它有一些缺点。
它有一个基于每一个轮循周期的必定延时,
并且它非常浪费带宽,由于即时没有消息准备发送或接收,轮循也是周期反复的。
可是。假设你要想要一个好用的老式的方案,那它就是的。
有一种基于轮循的略微精细点的方法,叫做长轮循。
在这样的模型中,通过使用HTTP 1.1的keep-alive机制。
假设server没有不论什么消息,那么网络连接就一直保持。
当server有新的信息时,它就立即发送它到连接以完毕请求。
在这样的情况下。降低了轮循的网络开销。
相比于更现代的WebSocket模式,它还是过时和低效的方法。
Server-Send Events是还有一个方式。
你能够使用:
var source = new EventSource(url)
来建立到server的连接。构造函数然后加入对source对象的监听器,
用来处理由server发来的消息。
这样的方式同意server直接发送消息,且你能即时收到。
但你使用的还是一个分立的通道,如XHR,来发送你的消息到server。,
这意味你被强制去管理和同步两个独立的管道。
这样的组合模式提供了一个实用的方案,它被用在了一些WebRTC演示样例应用中。
可是它还不是真正的双向管道。
2.10 MediaStream API
MediaStream API用来訪问本地输入设备,如摄像头和麦克风的媒体流。getUserMedia()方法是初始化訪问本地输入设备的主要方法。
每个MediaStream对象能够包括多个不同的MediaStreamTrack对象--它们每个都表示一个不同的输入媒体,
如源自不同输入源的视频或音频。
每一个MediaStreamTrack能够包括多个通道(如左右声道)。
通道是MediaStream API定义的最小单元。
MediaStream对象能够以两种主要方式输出。
首先,它们能够被用来渲染输出给 MediaElemet, 如 <video>或<audio>元素。
第二,它们能够被用来发送到一个RTCPeerConnection--它能够将媒体流发送到远端。
每一个MediaStreamTrack能够使用一系列状态来表示,
由MediaSourceStates对象的states()方法的返回值描写叙述。
每一个MediaStreamTrack相同提供了一系列能力。能够通过capabilities()方法来訪问。
在最上层。MediaStream对象能够触发一系列的事件,比如addtrack, removetrack,或ended.
而下层的MediaStreamTrack也能够触发一系列的事件,如started, mute, unmute, voerconstrainted 和 ended.
2.11 RTCPeerConnection API
RTCPeerConnection API是每一个WebRTC间P2P连接的核心。能够使用:
var peerconnection = RTCPeerConnection(configuration);
来创新一个RTCPeerConnection对象。
configuration变量包括了至少一个基本的 iceServers的名字--它是一个URL对象数组,
包括了STUN,TURNserver的信息。用来在候选查找。
peerconnection对象在随后的每一个client。依赖于它是呼叫者还是被呼叫者,在使用时会略微有些不同。
2.11.1 呼叫者流程
以下是peerconnection对象创建后,呼叫者的流程总结:
. 注冊 onicecandidate 句柄
. 注冊 onaddstream 句柄
. 注冊 message 句柄
. 使用 getUserMedia 訪问本地摄像头
. JSEP offer/answer 处理
1. 注冊 onicecandidate 句柄
首先,须要注冊一个onicecandidate句柄来发送不论什么ICE候选给其他端,
一旦收到后。就会使用当中一个信令通道。
2. 注冊 onaddstream 句柄
然后。一旦收到了远端的视频流,就须要注冊一个 onaddstream句柄来显示这个视频流。
3. 注冊 message 句柄
你的信令通常也须要有一个句柄被注冊,用来响应从其他端收到的消息。
假设消息中包括有 RTCIceCandidate对象,
那么应该使用addIceCandidate()方法将它加入到 peerconnection对象中。
假设消息中还包括有 RTCSessionDescription对象 ,
那么应该使用 setRemoteDescription()方法将它加入到 peerconnection对象中。
4. 使用 getUserMedia 訪问本地摄像头
然后,你能够使用 getUserMedia()来设置你的本地媒体流和本地页面的显示,
相同的。也须要使用 addStream()方法将它加入到 peerconnection对象中。
5. JSEP offer/answer 处理
如今,你须要使用 createOffer()方法来准备好開始协商,
并注冊一个回调句柄--它由RTCSessionDescription对象收到。
这个回调句柄应当使用 setLocalDescription()将RTCSessionDescription加入到你的peerconnection对象。
最后,你要通过信令通道将这个RTCSessionDescription发送到远端。
2.11.2 被呼叫者流程
The following is a summary of the callee's flow, which is very similar in a lot of
ways to the caller's flow, except that it responds to the offer with an answer:
以下是被呼叫都的流程总结,除了提供一个应答响应外。它和呼叫者的流程非常相似:
. 注冊 onicecandidate 句柄
. 注冊 onaddstream 句柄
. 注冊 消息句柄
. 使用 getUserMedia 訪问本地摄像头
. JSEP offer/answer 处理
1. 注冊 onicecandidate 句柄
Just like the caller, you start by registering an onicecandidate handler that sends
any ICE candidates to the other peer as they are received, using one of the signaling
channels described previously.
2. 注冊 onaddstream 句柄
Then, like the caller, you register an onaddstream handler that displays the video
stream once it is received from the remote peer.
3. 注冊 消息句柄
Like the caller, your signaling channel should also have a handler registered
that responds to messages received from the other peer. If the message contains
an RTCIceCandidate object, it should add those to the peerconnection
object using the addIceCandidate() method. And if the message contains an
RTCSessionDescription object, it should add those to the peerconnection
object using the setRemoteDescription() method.
4. 使用 getUserMedia 訪问本地摄像头
Then, like the caller, you can utilize getUserMedia() to set up your local media
stream and display that on your local page, and also add it to the peerconnection
object using the addStream() method.
5. JSEP offer/answer 处理
Here you differ from the caller and you play your part in the negotiation by
passing remoteDescription to the createAnswer() method and registering a
callback handler that receives an RTCSessionDescription object. This callback
handler should then add this RTCSessionDescription to your peerconnection
object using setLocalDescription(). And then finally, it should also send this
RTCSessionDescription to the remote peer through your signaling channel. It is
also important to note that this callee flow is all initiated after the offer is received
from the caller.
2.12 RTCPeerConnection在WebRTC架构中的位置
以下是整个WebRTC的架构图,它显示了隐藏在RTCPeerConnection API下的复杂结构。
图1 WebRTC架构图 来自 www.WebRTC.org
2.13 RTCdataChannel API
在端之间使用WebRTC做媒体流发送的同一时候,它还能够使用 DataChannelAPI传输二进制数据流。
这个API最准确的定义应该是 WebRTC DataChannel API,
它由:
var datachannel = peerconnection.createDataChannel(label);
创建。
它非常灵活。也非常强大,和WebSocket API的send()方法相似。