WebRTC in the real world: STUN, TURN and signaling

 

 

!WebRTC中文社区原创,转载请注明出处,谢谢,水平有限,部分意思可能不到位,建议参考英文原帖!
WebRTC 可以p2p视频通话
但是...
WebRTC 仍然需要几个服务:
  • 信令服务: 使客户端之间交换数据用来协调建立通话
  • NAT穿透服务:应付NATs和防火墙
这篇文章会教你怎么搭建信令服务,和用STUN/TURN服务去做nat穿透。另外,我们会解释WebRTC是怎么做到多端通话的。以及如何和VoIP/PSTN(电话)建立通话。
如果你对WebRTC还没有基础,我们强烈建议你先看下Getting Started With WebRTC


一.什么是信令服务(Signaling)?

信令是一个协调沟通的过程,为了让一个WebRTC应用发起一个“通话”,客户端间需要交换以下信令信息:
1.发起和关闭一个通话的控制信息;
2.错误信息;
3.媒体元数据,比如编码解码设置,带宽和媒体类型;
4.Key数据,用于确保安全通讯;
5.网络数据,比如主机在外网下的IP地址和端口。


客户端的信令处理需要一种来回传递信息的方法,这种机制没有被WebRTC定义,你需要自己去创建它。下面我们将描绘几种构建信令服务的方法。在此之前,先讲几个概念……

为什么WebRTC没有定义信令?
为了避免冗余和最大化兼容已经确立的技术,WebRTC没有指定信令的方法和协议。
-------------------------------
(WebRTC设计思想是完全指定和控制媒体层,但是让signaling层尽量脱离应用,原因是不同的应用可能会使用不同的协议,比如已经存在的SIP或者Jingle呼叫协议等。这份协议中,需要交换的关键信息是多媒体会议的描述信息,包括在媒体层确定必要的传输方式和 媒体配置信息)
------------------------------------------------
JSEP的结构同样避免了让浏览器保存状态信息,如果让浏览器成为一个保存信令状态的机器,会出现一个问题,就是每次当页面重载的时候,信令会丢失。所以更好的方案是用服务器保存信令状态。






JSEP协议要求端对端之间需要发起(offer)和回应(answer)上面提到的数据。
offer和answer用SDP(Session Description Protocol format信令描述协议格式)交流,像这样


如果想知道所有的SDP代表的意思,可以看下这个链接:IEFT examples

记住,WebRTC这样设计是为了让offer端和answer端能够在tweaked之前通过SDP文档设置好参数。

举个例子: apprtc.appspot.com 里的preferAudioCodec()方法用来设置默认的编解码方式和比特率,SDP用JavaScript比较难操作,未来的版本可能会用JSON代替,但是SDP还是有一些优势的。


二.RTCPeerConnection + 信令: offer,answer和candidate

RTCPeerConnection 是WebRTC客户端在两端建立音视频通讯的API。

初始化RTCPeerConnection进程需要两个步骤:
1.确定当期的媒体条件,例如分辨率,编解码能力。这些是给offer和answer的原始数据。
2.获得应用主机的网络地址(也就是candidate)


一旦这些本地数据被确定好了,就必须通过信令机制在不同端交换。


假设A想呼叫B,下面是整个offer/answer机制的细节:

1.A创建一个RTCPeerConnection对象。
2.A用RTCPeerConnection的createOffer()方法创建一个offer(用SDP协议描述)。
3.A用他的offer设置本地描述setLocalDescription()。
4.A序列化offer,并且用信令机制发送给B.
5.B用A的offer调用setRemoteDescription()设置对方的描述,B的RTCPeerConnection就知道了A的配置了。
6.B调用createAnswer(),如果成功会返回一个本地的session描述,既B的answer。
7.B用她的answer设置为本地的描述,通过调用setLocalDescription().设置本地描述
8.B用信令机制发送序列化后的answer给A。
9.A设置B的answer为对方session描述,通过调用setRemoteDescription()设置对方的描述.


(至此,A和B都设置了本地和对方的描述)


A和B还需要交换网络信息。'finding candidates' 指的是用ICE framework.去发现网络接口和端口。
1.A用一个onIceCandidate handler创建一个RTCPeerConnection对象。
2.当网络candidates有效时这个handler会被调用。
3.在这个handler里,A发送序列化的candidates数据给B,通过信令通道。
4.当B从A获得一个candidate信息,她调用addIceCandidate()去给对方描述添加candidate。

JSEP支持ICE Candidate Trickling技术(允许呼叫者在首次初始化offer后,逐次发送candidates给被呼叫者,这是为了让被呼叫者开始设置连接而不用等到全部的candidates到达)



WebRTC 的信令编码
下面是W3C code exampleW3C代码样例,概况了完整的signaling过程。

样例假设已经有了信令机制:SignalingChannel。Signaling 会在下面探讨比较多的细节。

  1. var signalingChannel = new SignalingChannel();
  2. var configuration = {
  3.   'iceServers': [{
  4.     'url': 'stun:stun.example.org'
  5.   }]
  6. };
  7. var pc;
  8. // call start() to initiate
  9. function start() {
  10.   pc = new RTCPeerConnection(configuration);
  11.   // send any ice candidates to the other peer
  12.   pc.onicecandidate = function (evt) {
  13.     if (evt.candidate)
  14.       signalingChannel.send(JSON.stringify({
  15.         'candidate': evt.candidate
  16.       }));
  17.   };
  18.   // let the 'negotiationneeded' event trigger offer generation
  19.   pc.onnegotiationneeded = function () {
  20.     pc.createOffer(localDescCreated, logError);
  21.   }
  22.   // once remote stream arrives, show it in the remote video element
  23.   pc.onaddstream = function (evt) {
  24.     remoteView.src = URL.createObjectURL(evt.stream);
  25.   };
  26.   // get a local stream, show it in a self-view and add it to be sent
  27.   navigator.getUserMedia({
  28.     'audio': true,
  29.     'video': true
  30.   }, function (stream) {
  31.     selfView.src = URL.createObjectURL(stream);
  32.     pc.addStream(stream);
  33.   }, logError);
  34. }
  35. function localDescCreated(desc) {
  36.   pc.setLocalDescription(desc, function () {
  37.     signalingChannel.send(JSON.stringify({
  38.       'sdp': pc.localDescription
  39.     }));
  40.   }, logError);
  41. }
  42. signalingChannel.onmessage = function (evt) {
  43.   if (!pc)
  44.     start();
  45.   var message = JSON.parse(evt.data);
  46.   if (message.sdp)
  47.     pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
  48.       // if we received an offer, we need to answer
  49.       if (pc.remoteDescription.type == 'offer')
  50.         pc.createAnswer(localDescCreated, logError);
  51.     }, logError);
  52.   else
  53.     pc.addIceCandidate(new RTCIceCandidate(message.candidate));
  54. };
  55. function logError(error) {
  56.   log(error.name + ': ' + error.message);
  57. }
复制代码


查看“单页面”视频聊天的例子simpl.info/pc.可以在 控制台的lgo看到offer/answer 和candidate 的交换过程。
如果你想了解更多,可以在Chrome浏览器打开 chrome://webrtc-internals 或在opera打开 opera://webrtc-internals下载完整的代码。


三.成员发现机制(Peer discovery)这里有个问题: 我怎么发现谁可以通话?

对于电话,我们有电话号码和目录。对于在线视频聊天,我们需要身份和业务管理系统和一种让用户开始会话的手段。
WebRTC apps需要一种 让客户端标示自己以便可以开始和加入会话的方法。

成员发现机制Peer discovery mechanisms没有被WebRTC定义,在这里我们不用做选择。
这个过程可以像发送一个URL地址这么简单,对于视频聊天应用,比如 talky.io, tawk.com and browsermeeting.com,你通过分享一个通用链接邀请别人进入一个会话。
开发者Chris Ball开发了一个有趣的实验:serverless-webrtc,可以让WebRTC呼叫参与者分享元数据,通过任何信息服务,比如IM,email或者信鸽。


四.怎么创建一个signling服务?
再说一遍:信令机制没有被WebRTC标准定义,无论你选择哪种 ,你需要一个中间服务器去交换信令信息和不同客户端间的应用数据。


庆幸的是,信令信息很小,大部分交换都是在通话开始的时候。


在测试 apprtc.appspot.com 和 samdutton-nodertc.jit.su 时,我们发现一个 视频会话,总共有大概30-45的信息被信令服务器处理,信息大小大概是10kB。


除了相对要求不高的带宽,WebRTC 信令服务器不用花费过多的内存和进程,因为只需要转发信息和保持很少的会议状态数据(比如那个客户端被连接了)

小贴士 :
信令机制不仅可以用来交换会话元数据,也能用来传达应用数据。它就是个信息服务。


五.从服务端推信息给客户端

一个信令服务器需要是双向的:客户端到服务器和服务器到客户端。
双向通讯违反了HTTP 客户端/服务端 请求/回复的模式,但是有一些发展多年的技术,例如long polling(长时间轮询) 被用来从服务端发送数据给一个运行中的web应用。

最近,EventSource API 被广泛的应用,它允许“服务端发送事件”:数据通过HTTP从服务端发送给浏览器。
这里有个简单的demo:simpl.info/es
EventSource被设计为一种消息传送方式,但是它可以跟XHR 结合做成一个交换signaling的服务:从一个呼叫者传递信息,由XHR 请求传递,推送给被呼叫者。

WebSocket 是一种更自然的解放方案,它是为了全双工 客户端-服务端通讯设计的(信息可以在同一时间在两个端传递)。
用纯WebSocket或者Server-Sent Events (EventSource) 做为signaling服务的优点是后端调用这些APIs可以用多种Web框架实现,在使用PHP,Python和Ruby的情况下。


大约有四分之三的浏览器支持WebSocket ,更重要的是,所有支持WebRTC的桌面浏览器和移动浏览器都支持WebSocket。


TLS(安全传输层协议)应该用于所有的链接,已确保信息不会被截断。
同时用proxy traversal减少问题(更多关于WebSocket 和proxy traversal的资料可以看WebRTC chapter 和WebSocket Cheat Sheet


apprtc.appspot.com 的信令是通过Google App Engine Channel API完成的,Google App Engine Channel API是使用了Comet技术(长时间轮询)让APP后端和web客户端 实现推送通讯功能。这里有个代码预演


另外一种方案,可以通过Ajax去轮询服务端获取signaling,但会导致一堆多余的网络请求,特别是在移动客户端。
在一个会话被确定后,用户仍然需要去轮询signaling信息,因为会话可能会被其他用户改变或者终止。
WebRTC》这本书就用了这种经过优化轮询频率的方法。


信令压缩
虽然一个信令服务器在每一个客户端中花费相当小的带宽和CPU,但是一个普遍使用的应用可能需要从不同的地点处理很多信息,并且有很多高的并发数。一个大流量的WebRTC 应用需要心理服务端去处理相当大的负荷。


这里我们不讲细节,下面有一些 处理高数据量,高性能的信息通讯设置:
1.XMPP,最初被称为Jabber:一种被开发用来即时通讯的协议,可以用来做signaling。服务端可以用 ejabberd andOpenfire实现。JavaScript客户端,例如 Strophe.js 使用BOSH去模仿双向通讯流,但因为各种原因,BOSH可能不像WebSocket那么有效率。(Jingle 是一种支持视频和语音的XMPP扩展,WebRTC从libjingle库(Jingle的C++实现库)里使用了网络和传输组件 )
2.像 ZeroMQ(据说TokBox服务端使用了)、OpenMQ的开源库。
3.使用支持WebSocket商业的云服务平台。
4.商业的WebRTC 平台,比如vLine.


开发者Phil Leggetter提供了一系列信息服务器和第三方库列表在Real-Time Web Technologies Guide



用Node开发基于Sockket.io的信令服务

下面有个例子,Socket.io可以轻易创建一个用于交换信息的服务。
Socket.io非常适合WebRTC 的信令,因为它就是以“rooms”的概念设计的。
这个demo不是一个产品级别的服务,但是能够应付小数量的用户。


Socket.io通过下面的回调使用WebSocket: Adobe Flash Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe and JSONP polling。
Socket.io也被移植到后端版本,但是最广为人知的是Node版本。
这个demo没有WebRTC,它只是展示怎么创建一个webapp的signaling。
用控制台查看log,去看下客户端加入一个房间和交换数据发生了什么变化。
WebRTC codelab会一步一步教你怎么整合这个demo变成一个完整的WEbRTC视频聊天应用。
你可以从step 5 of the codelab repo下载源码或者在samdutton-nodertc.jit.su运行(用两个浏览器打开这个链接 )

这是客户端的index.htl:



还有JavaScript文件main.js:





完整的服务端:

  1. var static = require('node-static');
  2. var http = require('http');
  3. var file = new(static.Server)();
  4. var app = http.createServer(function (req, res) {
  5.   file.serve(req, res);
  6. }).listen(2013);
  7. var io = require('socket.io').listen(app);
  8. io.sockets.on('connection', function (socket){
  9.   // convenience function to log server messages to the client
  10.   function log(){
  11.     var array = ['>>> Message from server: '];
  12.     for (var i = 0; i < arguments.length; i++) {
  13.       array.push(arguments[i]);
  14.     }
  15.       socket.emit('log', array);
  16.   }
  17.   socket.on('message', function (message) {
  18.     log('Got message:', message);
  19.     // for a real app, would be room only (not broadcast)
  20.     socket.broadcast.emit('message', message);
  21.   });
  22.   socket.on('create or join', function (room) {
  23.     var numClients = io.sockets.clients(room).length;
  24.     log('Room ' + room + ' has ' + numClients + ' client(s)');
  25.     log('Request to create or join room ' + room);
  26.     if (numClients === 0){
  27.       socket.join(room);
  28.       socket.emit('created', room);
  29.     } else if (numClients === 1) {
  30.       io.sockets.in(room).emit('join', room);
  31.       socket.join(room);
  32.       socket.emit('joined', room);
  33.     } else { // max two clients
  34.       socket.emit('full', room);
  35.     }
  36.     socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
  37.     socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);
  38.   });
  39. });
复制代码



要运行这个app,你需要安装Node, socket.io and node-static。可以在 nodejs.org下载Node,再安装 socket.io 和node-static,在终端运行Node Package Manager:
npm install socket.ionpm install node-static
启动服务,运行下面命令
node server.js
在浏览器打开 localhost:2013.用新的浏览器打开localhost:2013 ,用控制台看下发生了什么


使用 RTCDataChannel交换信息
初始化一个WebRTC会话,必须有一个信令 服务器。
然而,一旦两端确定了 一个通话,理论上,RTCDataChannel可以接替信令通道,这可以减少信号的延迟。
一旦信息直接在两端通讯,RTCDataChannel会帮忙减少带宽使用和进程开销。没有例子,但可以看下面:
信令性能和扩展性
1.RTCPeerConnection 不会搜集candidates,直到setLocalDescription() 被调用。这个被JSEP IETF draft.强制要求了。
2.利用Trickle ICE(看上面解释):接收到candidates后立即调用addIceCandidate(),

现成的信令服务 
这里有一些可以用的WebRTC signaling服务端:

如果你一点都不想编码,你可以用完整的商业WebRTC平台,像vLineOpenTok and Asterisk



爱立信创建了一个 signaling server using PHP on Apache,在WebRTC早期的时候,现在这个已经被弃用了,但是如果你考虑到相似的情况,这个代码还是值得一看的。


六.Signaling安全 

Security is the art of making nothing happen.


所有WebRTC 组件都被强制加密。
但是信令机制没有被WebRTC标准定义,所有确保信令安全取决于你,如果一个攻击者想去劫持信令,他们会导致会话中止,重定向链接和记录,改变或者注入内容。
 
一个牢固的信令最重要的功能是使用加密协议,HTTPS 和WSS (i.e TLS)可以确保信息不会非加密拦截。
同时,小心不要广播信令信息,不然攻击者可以使用相同的信令服务链接其他来电用户。


使用  TLS.去确保WebRTC应用的安全。


信令交互完之后,使用ICE去处理NATs和防火墙
对于元数据的信令,WebRTC应用可以使用中间服务,但实际的媒体和数据流在一个会话确立后,RTCPeerConnection 尝试去直连客户端:P2P


在一个简单的世界里,每一个WebRTC端都有一个唯一的地址,这样他可以与其他端交换数据,以便直接 通讯。







实际情况下,大多数设备都在一个或多个NAT层后面,有些有防毒软件阻碍确定的端口和协议,还有很多在代理和公司的防火墙后面。
防火墙和NAT实际上可能由一些类似家庭wifi路由器产生的。







WebRTC 可以使用ICE框架去克服真实世界的复杂网络。
为了实现这个功能,你的应用必须传ICE服务地址给RTCPeerConnection,如下所述。


ICE 试着寻找最佳路线去连接对方,它会并行的寻找所有可能性,然后选择最有效的可行方式。 
ICE首先会尝试用设备系统或网卡获取到的主机地址去建立连接;如果这个失败了(设备在NATs后面就会)ICE从STUN服务器获得外部的地址,如果这个也失败了,就用TURN中转服务器做通讯。


也就是说:
STUN服务器用来获取外部网络地址。
如果P2P失败的话,[size=14.4444446563721px]TURN服务器用来中继通讯。


每一个TURN服务器都支持STUN:一个TURN服务器是由一个STUN服务器加上中继功能。ICE也可以用来应付复杂的NAT设置:
事实上,NAT的”打洞“可能需要除了公共IP之外的端口地址。


WebRTC应用在iceServers配置对象(RTCPeerConnection constructor)里设置STUN and/or TURN服务器地址。
apprtc.appspot.com里这个值像这样:
  1. {
  2.   'iceServers': [
  3.     {
  4.       'url': 'stun:stun.l.google.com:19302'
  5.     },
  6.     {
  7.       'url': 'turn:192.158.29.39:3478?transport=udp',
  8.       'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
  9.       'username': '28224511:1379330808'
  10.     },
  11.     {
  12.       'url': 'turn:192.158.29.39:3478?transport=tcp',
  13.       'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
  14.       'username': '28224511:1379330808'
  15.     }
  16.   ]
  17. }
复制代码
 
一旦RTCPeerConnection 有了这些信息,ICE会自动启动:RTCPeerConnection 使用ICE框架计算出两端间最佳路线,需要STUN和TURN服务器。
 

STUNNATs会给它的设备提供一个内部网络IP地址,但这个地址不能在外网使用,因为没有外网的地址,所有WebRTC没办法做连接,为解决这个问题,WebRTC使用了STUN。

STUN服务架设在外网,它有一个简单的任务:获取一个发送请求的设备(运行在NAT后边的应用)的IP和端口,然后返回这个地址。换句话说,应用使用STUN服务器发现它的外网IP和端口,这个过程确保了一个WebRTC端获得它自己的公共地址,然后通过signaling机制发送这个信息给另一端,这样就可以建立起一个直接连接。(在实际中,不同的NATs有不同的工作方式,可能有多个NAT层,但是原理是一样的)


STUN服务器不需要做太多工作和存储太多东西,所以简单的STUN服务器可以应付大量的请求。


根据 webrtcstats.com的统计,使用STUN方式建立WebRTC通话的成功率有86%的。





TURN
RTCPeerConnection 会试着用UDP在两端建立一个直连,如果失败了,RTCPeerConnection 会改用TCP,如果这个再失败了,TURN服务器会被作为后备方案使用,在两端间中继数据。



重述:TURN是在两端间中转视频/语音/数据 流,而不是发送数据。

 

TURN 有个公共地址,所以每个端即使在防火墙或者代理后面,也能访问到。
TURN有个简单的任务,中转数据流,但不像STUN,TURN会花费大量带宽。所有,TURN需要够强壮。

 

图表表示TURN的作用:单纯的STUN不起作用,客户端就会转向使用TURN。



部署 STUN 和 TURN 服务器
作为测试,谷歌公布了一个公共的STUN服务,stun.l.google.com:19302, apprtc.appspot.com用的就是这个。
作为一个产品级别的 STUN/TURN服务器,我们建议使用 rfc5766-turn-server,STUN 和TURN的源码可以从code.google.com/p/rfc5766-turn-server获取,这个链接也包括了部署的资料。VM image for Amazon Web Services is also available.

本社区也发布了部署教程:部署教程

一个可代替的TURN服务器是restrund,可以在source code 下载到,下面介绍在谷歌Compute Engine部署resrund的步骤:

  • Open firewall as necessary, for tcp=443, udp/tcp=3478
  • Create four instances, one for each public IP, Standard Ubuntu 12.06 image
  • Set up local firewall config (allow ANY from ANY)
  • Install tools:
    sudo apt-get install make
    sudo apt-get install gcc
  • Install libre from creytiv.com/re.html
  • Fetch restund from creytiv.com/restund.html and unpack
  • wget hancke.name/restund-auth.patch and apply with patch -p1 < restund-auth.patch
  • Run make, sudo make install for libre and restund
  • Adapt restund.conf to your needs (replace IP addresses and make sure it contains the same shared secret) and copy to /etc
  • Copy restund/etc/restund to /etc/init.d/
  • Configure restund:
    Set LD_LIBRARY_PATH
    Copy restund.conf to /etc/restund.conf
    Set restund.conf to use the right 10. IP address
  • Run restund
  • Test using stund client from remote machine: ./client IP:port




七.突破p2p:多人会议WebRTC

你可以需要看下Justin Uberti提议的IETF标识:请求TURN服务的API

很容易想象到一些场景不只是一对一的视频通话,举个例子,公司小组需要一个视频会议,或者一个公开的演讲,一个演讲者面对数百(或者数千)的观看者。

一个WebRTC应用可以使用多个RTCPeerConnections,这样每一个端可以连接其他端形成一个网络。
talky.io就是使用这种方法实现,对于少数的用户,可以很好的工作。但是进程和带宽开销会非常大,特别是移动客户端。




在一个星型结构里,一个WebRTC客户端可以选择一个端去分布数据流给所有的用户,你可以自己设计重新分配机制的服务和构造区实现这种方式(werrtc.org提供了一个样例sample client application

从Chrome31和Opera18 开始,从一个RTCPeerConnection 获取的媒体流,可以作为对方的输入:这里有个demosimpl.info/multi。这样可以确保更灵活的结构,因为它可以允许web应用通过选择哪个用户可以连接去控制一个通话 路由。


多点控制部件MCU(Multipoint Control Unit)

大量用户通话的更好解决方案是使用Multipoint Control Unit(MCU)。这是一个在大量参与者间分布媒体的桥接服务器。MCUs可以在一个视频 会议里处理不同的分辨率,编解码,和帧速率。对于多端会议,有很多因素要考虑:最重要的是,从多个源里,怎么显示多个视频和混合音频。像 vLine 的云平台也在致力于优化传输路由。
 
你可以去买一个MCU硬件或者自己搭一个。




Cisco MCU背部
有几个开源的MCU硬件款可以选,例如 Licode(以前称为Lynckia) 生产的开源 MCU for WebRTC; OpenTok 平台的Mantis.



突破浏览器: VoIP, telephones 和 messaging
WebRTC 的标准让浏览器和不同设备不同平台,例如手机或者一个视频会议系统,进行通话称为可能。

SIP是一种信令协议,用来做VoIP和视频系统。为了让WebRTC和SIP端通讯,WebRTC需要一个代理服务器去调解信令。信令一定会经过网关,但是一旦会话建立,视频和语音就能在两端传输。


PSTN,公共电话交换网络,是旧式模拟电话的交换网络。为了WebRTC和电话进行通话,必须通过一个PSTN网关。
同理,要让WebRTC跟Jingle端(像IM客户端)通讯,需要一个中间XMPP服务器。
Jingle作为XMPP的扩展,用来实现视频和语音能够作为信息服务:现在的WebRTC就是基于C++实现libjingle 库发展来的,Jingle最初是Google Talk的技术。


一堆应用库,平台让WebRTC能在实际中通讯:




sipML5 的开发者也开发了webrtc2sip的网关
Tethr and Tropo have demonstrated a framework for disaster communications 'in a briefcase', using an OpenBTS cell to enable communications between feature phones and computers via WebRTC. Telephone communication without a carrier! 



发现更多

WebRTC codelab: 一步一步教你怎么打造一个视频和文本聊天应用,使用Socket.io Signaling服务。在Node上面跑。


2013 Google I/O WebRTC presentation WebRTC领头人Justin Uberti.

Chris Wilson 关于WebRTC的介绍:Introduction to WebRTC Apps.
WebRTC Book 这本书有很多关于数据、signaling、网络拓扑结构的细节。
WebRTC and Signaling: What Two Years Has Taught Us:介绍为什么把signaling脱离出标准是个好主意
A Practical Guide to Building WebRTC Apps:提供很多WebRTC的技术和基础建设信息。
WebRTC chapter 深入研究WebRTC的结构,使用案例和性能。

!WebRTC中文社区原创,转载请注明出处,谢谢,水平有限,部分意思可能不到位,建议参考英文原帖!
WebRTC 可以p2p视频通话
但是...
WebRTC 仍然需要几个服务:
  • 信令服务: 使客户端之间交换数据用来协调建立通话
  • NAT穿透服务:应付NATs和防火墙
这篇文章会教你怎么搭建信令服务,和用STUN/TURN服务去做nat穿透。另外,我们会解释WebRTC是怎么做到多端通话的。以及如何和VoIP/PSTN(电话)建立通话。
如果你对WebRTC还没有基础,我们强烈建议你先看下Getting Started With WebRTC

一.什么是信令服务(Signaling)?

信令是一个协调沟通的过程,为了让一个WebRTC应用发起一个“通话”,客户端间需要交换以下信令信息:
1.发起和关闭一个通话的控制信息;
2.错误信息;
3.媒体元数据,比如编码解码设置,带宽和媒体类型;
4.Key数据,用于确保安全通讯;
5.网络数据,比如主机在外网下的IP地址和端口。


客户端的信令处理需要一种来回传递信息的方法,这种机制没有被WebRTC定义,你需要自己去创建它。下面我们将描绘几种构建信令服务的方法。在此之前,先讲几个概念……

为什么WebRTC没有定义信令?
为了避免冗余和最大化兼容已经确立的技术,WebRTC没有指定信令的方法和协议。
-------------------------------
(WebRTC设计思想是完全指定和控制媒体层,但是让signaling层尽量脱离应用,原因是不同的应用可能会使用不同的协议,比如已经存在的SIP或者Jingle呼叫协议等。这份协议中,需要交换的关键信息是多媒体会议的描述信息,包括在媒体层确定必要的传输方式和 媒体配置信息)
------------------------------------------------
JSEP的结构同样避免了让浏览器保存状态信息,如果让浏览器成为一个保存信令状态的机器,会出现一个问题,就是每次当页面重载的时候,信令会丢失。所以更好的方案是用服务器保存信令状态。






JSEP协议要求端对端之间需要发起(offer)和回应(answer)上面提到的数据。
offer和answer用SDP(Session Description Protocol format信令描述协议格式)交流,像这样


如果想知道所有的SDP代表的意思,可以看下这个链接:IEFT examples

记住,WebRTC这样设计是为了让offer端和answer端能够在tweaked之前通过SDP文档设置好参数。

举个例子: apprtc.appspot.com 里的preferAudioCodec()方法用来设置默认的编解码方式和比特率,SDP用JavaScript比较难操作,未来的版本可能会用JSON代替,但是SDP还是有一些优势的。


二.RTCPeerConnection + 信令: offer,answer和candidate

RTCPeerConnection 是WebRTC客户端在两端建立音视频通讯的API。

初始化RTCPeerConnection进程需要两个步骤:
1.确定当期的媒体条件,例如分辨率,编解码能力。这些是给offer和answer的原始数据。
2.获得应用主机的网络地址(也就是candidate)


一旦这些本地数据被确定好了,就必须通过信令机制在不同端交换。


假设A想呼叫B,下面是整个offer/answer机制的细节:

1.A创建一个RTCPeerConnection对象。
2.A用RTCPeerConnection的createOffer()方法创建一个offer(用SDP协议描述)。
3.A用他的offer设置本地描述setLocalDescription()。
4.A序列化offer,并且用信令机制发送给B.
5.B用A的offer调用setRemoteDescription()设置对方的描述,B的RTCPeerConnection就知道了A的配置了。
6.B调用createAnswer(),如果成功会返回一个本地的session描述,既B的answer。
7.B用她的answer设置为本地的描述,通过调用setLocalDescription().设置本地描述
8.B用信令机制发送序列化后的answer给A。
9.A设置B的answer为对方session描述,通过调用setRemoteDescription()设置对方的描述.


(至此,A和B都设置了本地和对方的描述)


A和B还需要交换网络信息。'finding candidates' 指的是用ICE framework.去发现网络接口和端口。
1.A用一个onIceCandidate handler创建一个RTCPeerConnection对象。
2.当网络candidates有效时这个handler会被调用。
3.在这个handler里,A发送序列化的candidates数据给B,通过信令通道。
4.当B从A获得一个candidate信息,她调用addIceCandidate()去给对方描述添加candidate。

JSEP支持 ICE Candidate Trickling技术(允许呼叫者在首次初始化offer后,逐次发送candidates给被呼叫者,这是为了让被呼叫者开始设置连接而不用等到全部的candidates到达)



WebRTC 的信令编码
下面是W3C code exampleW3C代码样例,概况了完整的signaling过程。

样例假设已经有了信令机制:SignalingChannel。Signaling 会在下面探讨比较多的细节。
  1. var signalingChannel = new SignalingChannel();
  2. var configuration = {
  3.   'iceServers': [{
  4.     'url': 'stun:stun.example.org'
  5.   }]
  6. };
  7. var pc;
  8. // call start() to initiate
  9. function start() {
  10.   pc = new RTCPeerConnection(configuration);
  11.   // send any ice candidates to the other peer
  12.   pc.onicecandidate = function (evt) {
  13.     if (evt.candidate)
  14.       signalingChannel.send(JSON.stringify({
  15.         'candidate': evt.candidate
  16.       }));
  17.   };
  18.   // let the 'negotiationneeded' event trigger offer generation
  19.   pc.onnegotiationneeded = function () {
  20.     pc.createOffer(localDescCreated, logError);
  21.   }
  22.   // once remote stream arrives, show it in the remote video element
  23.   pc.onaddstream = function (evt) {
  24.     remoteView.src = URL.createObjectURL(evt.stream);
  25.   };
  26.   // get a local stream, show it in a self-view and add it to be sent
  27.   navigator.getUserMedia({
  28.     'audio': true,
  29.     'video': true
  30.   }, function (stream) {
  31.     selfView.src = URL.createObjectURL(stream);
  32.     pc.addStream(stream);
  33.   }, logError);
  34. }
  35. function localDescCreated(desc) {
  36.   pc.setLocalDescription(desc, function () {
  37.     signalingChannel.send(JSON.stringify({
  38.       'sdp': pc.localDescription
  39.     }));
  40.   }, logError);
  41. }
  42. signalingChannel.onmessage = function (evt) {
  43.   if (!pc)
  44.     start();
  45.   var message = JSON.parse(evt.data);
  46.   if (message.sdp)
  47.     pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
  48.       // if we received an offer, we need to answer
  49.       if (pc.remoteDescription.type == 'offer')
  50.         pc.createAnswer(localDescCreated, logError);
  51.     }, logError);
  52.   else
  53.     pc.addIceCandidate(new RTCIceCandidate(message.candidate));
  54. };
  55. function logError(error) {
  56.   log(error.name + ': ' + error.message);
  57. }
复制代码

查看“单页面”视频聊天的例子simpl.info/pc.可以在 控制台的lgo看到offer/answer 和candidate 的交换过程。
如果你想了解更多,可以在Chrome浏览器打开 chrome://webrtc-internals 或在opera打开 opera://webrtc-internals下载完整的代码。


三.成员发现机制(Peer discovery) 这里有个问题: 我怎么发现谁可以通话?

对于电话,我们有电话号码和目录。对于在线视频聊天,我们需要身份和业务管理系统和一种让用户开始会话的手段。
WebRTC apps需要一种 让客户端标示自己以便可以开始和加入会话的方法。

成员发现机制Peer discovery mechanisms没有被WebRTC定义,在这里我们不用做选择。
这个过程可以像发送一个URL地址这么简单,对于视频聊天应用,比如 talky.io, tawk.com and browsermeeting.com,你通过分享一个通用链接邀请别人进入一个会话。
开发者Chris Ball开发了一个有趣的实验:serverless-webrtc,可以让WebRTC呼叫参与者分享元数据,通过任何信息服务,比如IM,email或者信鸽。


四.怎么创建一个signling服务?
再说一遍: 信令机制没有被WebRTC标准定义,无论你选择哪种 ,你需要一个中间服务器去交换信令信息和不同客户端间的应用数据。


庆幸的是,信令信息很小,大部分交换都是在通话开始的时候。


在测试 apprtc.appspot.com 和 samdutton-nodertc.jit.su 时,我们发现一个 视频会话,总共有大概30-45的信息被信令服务器处理,信息大小大概是10kB。


除了相对要求不高的带宽,WebRTC 信令服务器不用花费过多的内存和进程,因为只需要转发信息和保持很少的会议状态数据(比如那个客户端被连接了)

小贴士 :
信令机制不仅可以用来交换会话元数据,也能用来传达应用数据。它就是个信息服务。


五.从服务端推信息给客户端

一个信令服务器需要是双向的:客户端到服务器和服务器到客户端。
双向通讯违反了HTTP 客户端/服务端 请求/回复的模式,但是有一些发展多年的技术,例如long polling(长时间轮询) 被用来从服务端发送数据给一个运行中的web应用。

最近,EventSource API 被广泛的应用,它允许“服务端发送事件”:数据通过HTTP从服务端发送给浏览器。
这里有个简单的demo:simpl.info/es
EventSource被设计为一种消息传送方式,但是它可以跟XHR 结合做成一个交换signaling的服务:从一个呼叫者传递信息,由XHR 请求传递,推送给被呼叫者。

WebSocket 是一种更自然的解放方案,它是为了全双工 客户端-服务端通讯设计的(信息可以在同一时间在两个端传递)。
用纯WebSocket或者Server-Sent Events (EventSource) 做为signaling服务的优点是后端调用这些APIs可以用多种Web框架实现,在使用PHP,Python和Ruby的情况下。


大约有四分之三的浏览器支持WebSocket ,更重要的是,所有支持WebRTC的桌面浏览器和移动浏览器都支持WebSocket。


TLS(安全传输层协议)应该用于所有的链接,已确保信息不会被截断。
同时用proxy traversal减少问题(更多关于WebSocket 和proxy traversal的资料可以看WebRTC chapter 和WebSocket Cheat Sheet


apprtc.appspot.com 的信令是通过Google App Engine Channel API完成的,Google App Engine Channel API是使用了Comet技术(长时间轮询)让APP后端和web客户端 实现推送通讯功能。这里有个代码预演


另外一种方案,可以通过Ajax去轮询服务端获取signaling,但会导致一堆多余的网络请求,特别是在移动客户端。
在一个会话被确定后,用户仍然需要去轮询signaling信息,因为会话可能会被其他用户改变或者终止。
WebRTC》这本书就用了这种经过优化轮询频率的方法。


信令压缩
虽然一个信令服务器在每一个客户端中花费相当小的带宽和CPU,但是一个普遍使用的应用可能需要从不同的地点处理很多信息,并且有很多高的并发数。一个大流量的WebRTC 应用需要心理服务端去处理相当大的负荷。


这里我们不讲细节,下面有一些 处理高数据量,高性能的信息通讯设置:
1.XMPP,最初被称为Jabber:一种被开发用来即时通讯的协议,可以用来做signaling。服务端可以用 ejabberd andOpenfire实现。JavaScript客户端,例如 Strophe.js 使用BOSH去模仿双向通讯流,但因为各种原因,BOSH可能不像WebSocket那么有效率。(Jingle 是一种支持视频和语音的XMPP扩展,WebRTC从libjingle库(Jingle的C++实现库)里使用了网络和传输组件 )
2.像 ZeroMQ(据说TokBox服务端使用了)、OpenMQ的开源库。
3.使用支持WebSocket商业的云服务平台。
4.商业的WebRTC 平台,比如vLine.


开发者Phil Leggetter提供了一系列信息服务器和第三方库列表在Real-Time Web Technologies Guide



用Node开发基于Sockket.io的信令服务

下面有个例子,Socket.io可以轻易创建一个用于交换信息的服务。
Socket.io非常适合WebRTC 的信令,因为它就是以“rooms”的概念设计的。
这个demo不是一个产品级别的服务,但是能够应付小数量的用户。


Socket.io通过下面的回调使用WebSocket: Adobe Flash Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe and JSONP polling。
Socket.io也被移植到后端版本,但是最广为人知的是Node版本。
这个demo没有WebRTC,它只是展示怎么创建一个webapp的signaling。
用控制台查看log,去看下客户端加入一个房间和交换数据发生了什么变化。
WebRTC codelab会一步一步教你怎么整合这个demo变成一个完整的WEbRTC视频聊天应用。
你可以从step 5 of the codelab repo下载源码或者在samdutton-nodertc.jit.su运行(用两个浏览器打开这个链接 )

这是客户端的index.htl:



还有JavaScript文件main.js:





完整的服务端:
  1. var static = require('node-static');
  2. var http = require('http');
  3. var file = new(static.Server)();
  4. var app = http.createServer(function (req, res) {
  5.   file.serve(req, res);
  6. }).listen(2013);
  7. var io = require('socket.io').listen(app);
  8. io.sockets.on('connection', function (socket){
  9.   // convenience function to log server messages to the client
  10.   function log(){
  11.     var array = ['>>> Message from server: '];
  12.     for (var i = 0; i < arguments.length; i++) {
  13.       array.push(arguments[i]);
  14.     }
  15.       socket.emit('log', array);
  16.   }
  17.   socket.on('message', function (message) {
  18.     log('Got message:', message);
  19.     // for a real app, would be room only (not broadcast)
  20.     socket.broadcast.emit('message', message);
  21.   });
  22.   socket.on('create or join', function (room) {
  23.     var numClients = io.sockets.clients(room).length;
  24.     log('Room ' + room + ' has ' + numClients + ' client(s)');
  25.     log('Request to create or join room ' + room);
  26.     if (numClients === 0){
  27.       socket.join(room);
  28.       socket.emit('created', room);
  29.     } else if (numClients === 1) {
  30.       io.sockets.in(room).emit('join', room);
  31.       socket.join(room);
  32.       socket.emit('joined', room);
  33.     } else { // max two clients
  34.       socket.emit('full', room);
  35.     }
  36.     socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
  37.     socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);
  38.   });
  39. });
复制代码


要运行这个app,你需要安装Node, socket.io and node-static。可以在 nodejs.org下载Node,再安装 socket.io 和node-static,在终端运行Node Package Manager:
npm install socket.ionpm install node-static
启动服务,运行下面命令
node server.js
在浏览器打开 localhost:2013.用新的浏览器打开localhost:2013 ,用控制台看下发生了什么


使用 RTCDataChannel交换信息
初始化一个WebRTC会话,必须有一个信令 服务器。
然而,一旦两端确定了 一个通话,理论上,RTCDataChannel可以接替信令通道,这可以减少信号的延迟。
一旦信息直接在两端通讯,RTCDataChannel会帮忙减少带宽使用和进程开销。没有例子,但可以看下面:
信令性能和扩展性
1.RTCPeerConnection 不会搜集candidates,直到setLocalDescription() 被调用。这个被JSEP IETF draft.强制要求了。
2.利用Trickle ICE(看上面解释):接收到candidates后立即调用addIceCandidate(),

现成的信令服务 
这里有一些可以用的WebRTC signaling服务端:

如果你一点都不想编码,你可以用完整的商业WebRTC平台,像vLineOpenTok and Asterisk



爱立信创建了一个 signaling server using PHP on Apache,在WebRTC早期的时候,现在这个已经被弃用了,但是如果你考虑到相似的情况,这个代码还是值得一看的。


六.Signaling安全 
Security is the art of making nothing happen.

所有WebRTC 组件都被强制加密。
但是信令机制没有被WebRTC标准定义,所有确保信令安全取决于你,如果一个攻击者想去劫持信令,他们会导致会话中止,重定向链接和记录,改变或者注入内容。
 
一个牢固的信令最重要的功能是使用加密协议,HTTPS 和WSS (i.e TLS)可以确保信息不会非加密拦截。
同时,小心不要广播信令信息,不然攻击者可以使用相同的信令服务链接其他来电用户。

使用  TLS.去确保WebRTC应用的安全。

信令交互完之后,使用ICE去处理NATs和防火墙
对于元数据的信令,WebRTC应用可以使用中间服务,但实际的媒体和数据流在一个会话确立后,RTCPeerConnection 尝试去直连客户端:P2P


在一个简单的世界里,每一个WebRTC端都有一个唯一的地址,这样他可以与其他端交换数据,以便直接 通讯。







实际情况下,大多数设备都在一个或多个NAT层后面,有些有防毒软件阻碍确定的端口和协议,还有很多在代理和公司的防火墙后面。
防火墙和NAT实际上可能由一些类似家庭wifi路由器产生的。







WebRTC 可以使用ICE框架去克服真实世界的复杂网络。
为了实现这个功能,你的应用必须传ICE服务地址给RTCPeerConnection,如下所述。


ICE 试着寻找最佳路线去连接对方,它会并行的寻找所有可能性,然后选择最有效的可行方式。 
ICE首先会尝试用设备系统或网卡获取到的主机地址去建立连接;如果这个失败了(设备在NATs后面就会)ICE从STUN服务器获得外部的地址,如果这个也失败了,就用TURN中转服务器做通讯。


也就是说:
STUN服务器用来获取外部网络地址。
如果P2P失败的话,[size=14.4444446563721px]TURN服务器用来中继通讯。

每一个TURN服务器都支持STUN:一个TURN服务器是由一个STUN服务器加上中继功能。ICE也可以用来应付复杂的NAT设置:
事实上,NAT的”打洞“可能需要除了公共IP之外的端口地址。

WebRTC应用在iceServers配置对象(RTCPeerConnection constructor)里设置STUN and/or TURN服务器地址。
apprtc.appspot.com里这个值像这样:
  1. {
  2.   'iceServers': [
  3.     {
  4.       'url': 'stun:stun.l.google.com:19302'
  5.     },
  6.     {
  7.       'url': 'turn:192.158.29.39:3478?transport=udp',
  8.       'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
  9.       'username': '28224511:1379330808'
  10.     },
  11.     {
  12.       'url': 'turn:192.158.29.39:3478?transport=tcp',
  13.       'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
  14.       'username': '28224511:1379330808'
  15.     }
  16.   ]
  17. }
复制代码
 
一旦RTCPeerConnection 有了这些信息,ICE会自动启动:RTCPeerConnection 使用ICE框架计算出两端间最佳路线,需要STUN和TURN服务器。
 
STUN NATs会给它的设备提供一个内部网络IP地址,但这个地址不能在外网使用,因为没有外网的地址,所有WebRTC没办法做连接,为解决这个问题,WebRTC使用了STUN。

STUN服务架设在外网,它有一个简单的任务:获取一个发送请求的设备(运行在NAT后边的应用)的IP和端口,然后返回这个地址。换句话说,应用使用STUN服务器发现它的外网IP和端口,这个过程确保了一个WebRTC端获得它自己的公共地址,然后通过signaling机制发送这个信息给另一端,这样就可以建立起一个直接连接。(在实际中,不同的NATs有不同的工作方式,可能有多个NAT层,但是原理是一样的)


STUN服务器不需要做太多工作和存储太多东西,所以简单的STUN服务器可以应付大量的请求。


根据   webrtcstats.com 的统计,使用STUN方式建立WebRTC通话的成功率有86%的。





TURN
RTCPeerConnection 会试着用UDP在两端建立一个直连,如果失败了, RTCPeerConnection  会改用TCP,如果这个再失败了,TURN服务器会被作为后备方案使用,在两端间中继数据。




重述:TURN是在两端间中转视频/语音/数据 流,而不是发送数据。

TURN 有个公共地址,所以每个端即使在防火墙或者代理后面,也能访问到。
TURN有个简单的任务,中转数据流,但不像STUN,TURN会花费大量带宽。所有,TURN需要够强壮。

图表表示TURN的作用:单纯的STUN不起作用,客户端就会转向使用TURN。


部署 STUN 和 TURN 服务器
作为测试,谷歌公布了一个公共的STUN服务, stun.l.google.com:19302,   apprtc.appspot.com 用的就是这个。
作为一个产品级别的   STUN/TURN服务器,我们建议使用   rfc5766-turn-server, STUN 和TURN的源码可以从 code.google.com/p/rfc5766-turn-server 获取,这个链接也包括了部署的资料。 VM image for Amazon Web Services  is also available.

本社区也发布了部署教程:部署教程

一个可代替的TURN服务器是restrund,可以在 source code  下载到, 下面介绍在谷歌Compute Engine部署resrund的步骤:
  • Open firewall as necessary, for tcp=443, udp/tcp=3478
  • Create four instances, one for each public IP, Standard Ubuntu 12.06 image
  • Set up local firewall config (allow ANY from ANY)
  • Install tools:
    sudo apt-get install make
    sudo apt-get install gcc
  • Install libre from creytiv.com/re.html
  • Fetch restund from creytiv.com/restund.html and unpack
  • wget hancke.name/restund-auth.patch and apply with patch -p1 < restund-auth.patch
  • Run make, sudo make install for libre and restund
  • Adapt restund.conf to your needs (replace IP addresses and make sure it contains the same shared secret) and copy to /etc
  • Copy restund/etc/restund to /etc/init.d/
  • Configure restund:
    Set LD_LIBRARY_PATH
    Copy restund.conf to /etc/restund.conf
    Set restund.conf to use the right 10. IP address
  • Run restund
  • Test using stund client from remote machine: ./client IP:port



七.突破p2p:多人会议WebRTC

你可以需要看下Justin Uberti提议的IETF标识:请求TURN服务的API

很容易想象到一些场景不只是一对一的视频通话,举个例子,公司小组需要一个视频会议,或者一个公开的演讲,一个演讲者面对数百(或者数千)的观看者。

一个WebRTC应用可以使用多个RTCPeerConnections,这样每一个端可以连接其他端形成一个网络。
talky.io就是使用这种方法实现,对于少数的用户,可以很好的工作。但是进程和带宽开销会非常大,特别是移动客户端。




在一个星型结构里,一个WebRTC客户端可以选择一个端去分布数据流给所有的用户,你可以自己设计重新分配机制的服务和构造区实现这种方式(werrtc.org提供了一个样例sample client application

从Chrome31和Opera18 开始,从一个RTCPeerConnection 获取的媒体流,可以作为对方的输入:这里有个demosimpl.info/multi。这样可以确保更灵活的结构,因为它可以允许web应用通过选择哪个用户可以连接去控制一个通话 路由。


多点控制部件MCU(Multipoint Control Unit)
大量用户通话的更好解决方案是使用Multipoint Control Unit(MCU)。这是一个在大量参与者间分布媒体的桥接服务器。MCUs可以在一个视频 会议里处理不同的分辨率,编解码,和帧速率。对于多端会议,有很多因素要考虑:最重要的是,从多个源里,怎么显示多个视频和混合音频。像 vLine 的云平台也在致力于优化传输路由。
 
你可以去买一个MCU硬件或者自己搭一个。



Cisco MCU背部
有几个开源的MCU硬件款可以选,例如 Licode(以前称为Lynckia) 生产的开源 MCU for WebRTC; OpenTok 平台的Mantis.



突破浏览器: VoIP, telephones 和 messaging
WebRTC 的标准让浏览器和不同设备不同平台,例如手机或者一个视频会议系统,进行通话称为可能。

SIP是一种信令协议,用来做VoIP和视频系统。为了让WebRTC和SIP端通讯,WebRTC需要一个代理服务器去调解信令。信令一定会经过网关,但是一旦会话建立,视频和语音就能在两端传输。


PSTN,公共电话交换网络,是旧式模拟电话的交换网络。为了WebRTC和电话进行通话,必须通过一个PSTN网关。
同理,要让WebRTC跟Jingle端(像IM客户端)通讯,需要一个中间XMPP服务器。
Jingle作为XMPP的扩展,用来实现视频和语音能够作为信息服务:现在的WebRTC就是基于C++实现libjingle 库发展来的,Jingle最初是Google Talk的技术。


一堆应用库,平台让WebRTC能在实际中通讯:





sipML5 的开发者也开发了webrtc2sip的网关
Tethr and Tropo have demonstrated a framework for disaster communications 'in a briefcase', using an OpenBTS cell to enable communications between feature phones and computers via WebRTC. Telephone communication without a carrier! 



发现更多

WebRTC codelab: 一步一步教你怎么打造一个视频和文本聊天应用,使用Socket.io Signaling服务。在Node上面跑。


2013 Google I/O WebRTC presentation WebRTC领头人Justin Uberti.
Chris Wilson 关于WebRTC的介绍:Introduction to WebRTC Apps.
WebRTC Book 这本书有很多关于数据、signaling、网络拓扑结构的细节。
WebRTC and Signaling: What Two Years Has Taught Us:介绍为什么把signaling脱离出标准是个好主意
A Practical Guide to Building WebRTC Apps:提供很多WebRTC的技术和基础建设信息。
WebRTC chapter 深入研究WebRTC的结构,使用案例和性能。

转载于:https://www.cnblogs.com/bktmkd/p/5100082.html

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
提供的源码资源涵盖了安卓应用、小程序、Python应用和Java应用等多个领域,每个领域都包含了丰富的实例和项目。这些源码都是基于各自平台的最新技术和标准编写,确保了在对应环境下能够无缝运行。同时,源码中配备了详细的注释和文档,帮助用户快速理解代码结构和实现逻辑。 适用人群: 这些源码资源特别适合大学生群体。无论你是计算机相关专业的学生,还是对其他领域编程感兴趣的学生,这些资源都能为你提供宝贵的学习和实践机会。通过学习和运行这些源码,你可以掌握各平台开发的基础知识,提升编程能力和项目实战经验。 使用场景及目标: 在学习阶段,你可以利用这些源码资源进行课程实践、课外项目或毕业设计。通过分析和运行源码,你将深入了解各平台开发的技术细节和最佳实践,逐步培养起自己的项目开发和问题解决能力。此外,在求职或创业过程中,具备跨平台开发能力的大学生将更具竞争力。 其他说明: 为了确保源码资源的可运行性和易用性,特别注意了以下几点:首先,每份源码都提供了详细的运行环境和依赖说明,确保用户能够轻松搭建起开发环境;其次,源码中的注释和文档都非常完善,方便用户快速上手和理解代码;最后,我会定期更新这些源码资源,以适应各平台技术的最新发展和市场需求。

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值