WebRTC in the real world: STUN, TURN and signaling

翻譯?http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/

筋遭!WebRTC中文社區(qū)原創(chuàng)慢睡,轉(zhuǎn)載請(qǐng)注明出處痴施,謝謝,水平有限宇驾,部分意思可能不到位寺谤,建議參考英文原帖希俩!
WebRTC 可以p2p視頻通話(huà)
但是...
WebRTC 仍然需要幾個(gè)服務(wù):
信令服務(wù): 使客戶(hù)端之間交換數(shù)據(jù)用來(lái)協(xié)調(diào)建立通話(huà)
NAT穿透服務(wù):應(yīng)付NATs和防火墻

這篇文章會(huì)教你怎么搭建信令服務(wù)档押,和用STUN/TURN服務(wù)去做nat穿透逞力。另外煌往,我們會(huì)解釋W(xué)ebRTC是怎么做到多端通話(huà)的倾哺。以及如何和VoIP/PSTN(電話(huà))建立通話(huà)。
如果你對(duì)WebRTC還沒(méi)有基礎(chǔ)刽脖,我們強(qiáng)烈建議你先看下Getting Started With WebRTC羞海。
一.什么是信令服務(wù)(Signaling)?信令是一個(gè)協(xié)調(diào)溝通的過(guò)程曲管,為了讓一個(gè)WebRTC應(yīng)用發(fā)起一個(gè)“通話(huà)”却邓,客戶(hù)端間需要交換以下信令信息:1.發(fā)起和關(guān)閉一個(gè)通話(huà)的控制信息;2.錯(cuò)誤信息院水;3.媒體元數(shù)據(jù)腊徙,比如編碼解碼設(shè)置,帶寬和媒體類(lèi)型檬某;4.Key數(shù)據(jù)撬腾,用于確保安全通訊;5.網(wǎng)絡(luò)數(shù)據(jù)恢恼,比如主機(jī)在外網(wǎng)下的IP地址和端口民傻。客戶(hù)端的信令處理需要一種來(lái)回傳遞信息的方法,這種機(jī)制沒(méi)有被WebRTC定義漓踢,你需要自己去創(chuàng)建它牵署。下面我們將描繪幾種構(gòu)建信令服務(wù)的方法。在此之前喧半,先講幾個(gè)概念……為什么WebRTC沒(méi)有定義信令碟刺?為了避免冗余和最大化兼容已經(jīng)確立的技術(shù),WebRTC沒(méi)有指定信令的方法和協(xié)議薯酝。-------------------------------(WebRTC設(shè)計(jì)思想是完全指定和控制媒體層半沽,但是讓signaling層盡量脫離應(yīng)用,原因是不同的應(yīng)用可能會(huì)使用不同的協(xié)議吴菠,比如已經(jīng)存在的SIP或者Jingle呼叫協(xié)議等者填。這份協(xié)議中,需要交換的關(guān)鍵信息是多媒體會(huì)議的描述信息做葵,包括在媒體層確定必要的傳輸方式和 媒體配置信息)------------------------------------------------JSEP的結(jié)構(gòu)同樣避免了讓瀏覽器保存狀態(tài)信息占哟,如果讓瀏覽器成為一個(gè)保存信令狀態(tài)的機(jī)器,會(huì)出現(xiàn)一個(gè)問(wèn)題酿矢,就是每次當(dāng)頁(yè)面重載的時(shí)候榨乎,信令會(huì)丟失。所以更好的方案是用服務(wù)器保存信令狀態(tài)瘫筐。

JSEP協(xié)議要求端對(duì)端之間需要發(fā)起(offer)和回應(yīng)(answer)上面提到的數(shù)據(jù)蜜暑。offer和answer用SDP(Session Description Protocol format信令描述協(xié)議格式)交流,像這樣:
如果想知道所有的SDP代表的意思策肝,可以看下這個(gè)鏈接:IEFT examples記住肛捍,WebRTC這樣設(shè)計(jì)是為了讓offer端和answer端能夠在tweaked之前通過(guò)SDP文檔設(shè)置好參數(shù)。舉個(gè)例子: apprtc.appspot.com 里的preferAudioCodec()方法用來(lái)設(shè)置默認(rèn)的編解碼方式和比特率之众,SDP用JavaScript比較難操作拙毫,未來(lái)的版本可能會(huì)用JSON代替,但是SDP還是有一些優(yōu)勢(shì)的棺禾。二.RTCPeerConnection + 信令: offer,answer和candidateRTCPeerConnection 是WebRTC客戶(hù)端在兩端建立音視頻通訊的API缀蹄。初始化RTCPeerConnection進(jìn)程需要兩個(gè)步驟:1.確定當(dāng)期的媒體條件,例如分辨率膘婶,編解碼能力缺前。這些是給offer和answer的原始數(shù)據(jù)。2.獲得應(yīng)用主機(jī)的網(wǎng)絡(luò)地址(也就是candidate)一旦這些本地?cái)?shù)據(jù)被確定好了竣付,就必須通過(guò)信令機(jī)制在不同端交換诡延。假設(shè)A想呼叫B滞欠,下面是整個(gè)offer/answer機(jī)制的細(xì)節(jié):1.A創(chuàng)建一個(gè)RTCPeerConnection對(duì)象古胆。2.A用RTCPeerConnection的createOffer()方法創(chuàng)建一個(gè)offer(用SDP協(xié)議描述)。3.A用他的offer設(shè)置本地描述setLocalDescription()。4.A序列化offer逸绎,并且用信令機(jī)制發(fā)送給B.5.B用A的offer調(diào)用setRemoteDescription()設(shè)置對(duì)方的描述惹恃,B的RTCPeerConnection就知道了A的配置了。6.B調(diào)用createAnswer()棺牧,如果成功會(huì)返回一個(gè)本地的session描述巫糙,既B的answer。7.B用她的answer設(shè)置為本地的描述颊乘,通過(guò)調(diào)用setLocalDescription().設(shè)置本地描述8.B用信令機(jī)制發(fā)送序列化后的answer給A参淹。9.A設(shè)置B的answer為對(duì)方session描述,通過(guò)調(diào)用setRemoteDescription()設(shè)置對(duì)方的描述.(至此乏悄,A和B都設(shè)置了本地和對(duì)方的描述)A和B還需要交換網(wǎng)絡(luò)信息浙值。'finding candidates' 指的是用ICE framework.去發(fā)現(xiàn)網(wǎng)絡(luò)接口和端口。1.A用一個(gè)onIceCandidate handler創(chuàng)建一個(gè)RTCPeerConnection對(duì)象檩小。2.當(dāng)網(wǎng)絡(luò)candidates有效時(shí)這個(gè)handler會(huì)被調(diào)用开呐。3.在這個(gè)handler里,A發(fā)送序列化的candidates數(shù)據(jù)給B规求,通過(guò)信令通道筐付。4.當(dāng)B從A獲得一個(gè)candidate信息,她調(diào)用addIceCandidate()去給對(duì)方描述添加candidate阻肿。JSEP支持ICE Candidate Trickling技術(shù)(允許呼叫者在首次初始化offer后瓦戚,逐次發(fā)送candidates給被呼叫者,這是為了讓被呼叫者開(kāi)始設(shè)置連接而不用等到全部的candidates到達(dá))WebRTC 的信令編碼下面是W3C code exampleW3C代碼樣例丛塌,概況了完整的signaling過(guò)程伤极。樣例假設(shè)已經(jīng)有了信令機(jī)制:SignalingChannel。Signaling 會(huì)在下面探討比較多的細(xì)節(jié)姨伤。
var signalingChannel = new SignalingChannel();
var configuration = {
'iceServers': [{
'url': 'stun:stun.example.org'
}]
};
var pc;

// call start() to initiate

function start() {
pc = new RTCPeerConnection(configuration);

// send any ice candidates to the other peer
pc.onicecandidate = function (evt) {
if (evt.candidate)
signalingChannel.send(JSON.stringify({
'candidate': evt.candidate
}));
};

// let the 'negotiationneeded' event trigger offer generation
pc.onnegotiationneeded = function () {
pc.createOffer(localDescCreated, logError);
}

// once remote stream arrives, show it in the remote video element
pc.onaddstream = function (evt) {
remoteView.src = URL.createObjectURL(evt.stream);
};

// get a local stream, show it in a self-view and add it to be sent
navigator.getUserMedia({
'audio': true,
'video': true
}, function (stream) {
selfView.src = URL.createObjectURL(stream);
pc.addStream(stream);
}, logError);
}

function localDescCreated(desc) {
pc.setLocalDescription(desc, function () {
signalingChannel.send(JSON.stringify({
'sdp': pc.localDescription
}));
}, logError);
}

signalingChannel.onmessage = function (evt) {
if (!pc)
start();

var message = JSON.parse(evt.data);
if (message.sdp)
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
// if we received an offer, we need to answer
if (pc.remoteDescription.type == 'offer')
pc.createAnswer(localDescCreated, logError);
}, logError);
else
pc.addIceCandidate(new RTCIceCandidate(message.candidate));
};

function logError(error) {
log(error.name + ': ' + error.message);
}

復(fù)制代碼
查看“單頁(yè)面”視頻聊天的例子simpl.info/pc.可以在 控制臺(tái)的lgo看到offer/answer 和candidate 的交換過(guò)程哨坪。如果你想了解更多,可以在Chrome瀏覽器打開(kāi) chrome://webrtc-internals 或在opera打開(kāi) opera://webrtc-internals下載完整的代碼乍楚。三.成員發(fā)現(xiàn)機(jī)制(Peer discovery)這里有個(gè)問(wèn)題: 我怎么發(fā)現(xiàn)誰(shuí)可以通話(huà)当编?對(duì)于電話(huà),我們有電話(huà)號(hào)碼和目錄徒溪。對(duì)于在線(xiàn)視頻聊天忿偷,我們需要身份和業(yè)務(wù)管理系統(tǒng)和一種讓用戶(hù)開(kāi)始會(huì)話(huà)的手段。WebRTC apps需要一種 讓客戶(hù)端標(biāo)示自己以便可以開(kāi)始和加入會(huì)話(huà)的方法臊泌。成員發(fā)現(xiàn)機(jī)制Peer discovery mechanisms沒(méi)有被WebRTC定義鲤桥,在這里我們不用做選擇。這個(gè)過(guò)程可以像發(fā)送一個(gè)URL地址這么簡(jiǎn)單渠概,對(duì)于視頻聊天應(yīng)用茶凳,比如 talky.io, tawk.com and browsermeeting.com嫂拴,你通過(guò)分享一個(gè)通用鏈接邀請(qǐng)別人進(jìn)入一個(gè)會(huì)話(huà)。開(kāi)發(fā)者Chris Ball開(kāi)發(fā)了一個(gè)有趣的實(shí)驗(yàn):serverless-webrtc贮喧,可以讓W(xué)ebRTC呼叫參與者分享元數(shù)據(jù)筒狠,通過(guò)任何信息服務(wù),比如IM箱沦,email或者信鴿辩恼。四.怎么創(chuàng)建一個(gè)signling服務(wù)?再說(shuō)一遍:信令機(jī)制沒(méi)有被WebRTC標(biāo)準(zhǔn)定義谓形,無(wú)論你選擇哪種 灶伊,你需要一個(gè)中間服務(wù)器去交換信令信息和不同客戶(hù)端間的應(yīng)用數(shù)據(jù)。慶幸的是寒跳,信令信息很小谁帕,大部分交換都是在通話(huà)開(kāi)始的時(shí)候。在測(cè)試 apprtc.appspot.comsamdutton-nodertc.jit.su 時(shí)冯袍,我們發(fā)現(xiàn)一個(gè) 視頻會(huì)話(huà)匈挖,總共有大概30-45的信息被信令服務(wù)器處理,信息大小大概是10kB康愤。除了相對(duì)要求不高的帶寬儡循,WebRTC 信令服務(wù)器不用花費(fèi)過(guò)多的內(nèi)存和進(jìn)程,因?yàn)橹恍枰D(zhuǎn)發(fā)信息和保持很少的會(huì)議狀態(tài)數(shù)據(jù)(比如那個(gè)客戶(hù)端被連接了)小貼士 :信令機(jī)制不僅可以用來(lái)交換會(huì)話(huà)元數(shù)據(jù)征冷,也能用來(lái)傳達(dá)應(yīng)用數(shù)據(jù)择膝。它就是個(gè)信息服務(wù)。五.從服務(wù)端推信息給客戶(hù)端一個(gè)信令服務(wù)器需要是雙向的:客戶(hù)端到服務(wù)器和服務(wù)器到客戶(hù)端检激。雙向通訊違反了HTTP 客戶(hù)端/服務(wù)端 請(qǐng)求/回復(fù)的模式肴捉,但是有一些發(fā)展多年的技術(shù),例如long polling(長(zhǎng)時(shí)間輪詢(xún)) 被用來(lái)從服務(wù)端發(fā)送數(shù)據(jù)給一個(gè)運(yùn)行中的web應(yīng)用叔收。最近齿穗,EventSource API 被廣泛的應(yīng)用,它允許“服務(wù)端發(fā)送事件”:數(shù)據(jù)通過(guò)HTTP從服務(wù)端發(fā)送給瀏覽器饺律。這里有個(gè)簡(jiǎn)單的demo:simpl.info/es窃页。EventSource被設(shè)計(jì)為一種消息傳送方式,但是它可以跟XHR 結(jié)合做成一個(gè)交換signaling的服務(wù):從一個(gè)呼叫者傳遞信息复濒,由XHR 請(qǐng)求傳遞脖卖,推送給被呼叫者。WebSocket 是一種更自然的解放方案巧颈,它是為了全雙工 客戶(hù)端-服務(wù)端通訊設(shè)計(jì)的(信息可以在同一時(shí)間在兩個(gè)端傳遞)畦木。用純WebSocket或者Server-Sent Events (EventSource) 做為signaling服務(wù)的優(yōu)點(diǎn)是后端調(diào)用這些APIs可以用多種Web框架實(shí)現(xiàn),在使用PHP,Python和Ruby的情況下砸泛。大約有四分之三的瀏覽器支持WebSocket ,更重要的是十籍,所有支持WebRTC的桌面瀏覽器和移動(dòng)瀏覽器都支持WebSocket蛆封。TLS(安全傳輸層協(xié)議)應(yīng)該用于所有的鏈接,已確保信息不會(huì)被截?cái)嗉宋怼M瑫r(shí)用proxy traversal減少問(wèn)題(更多關(guān)于WebSocket 和proxy traversal的資料可以看WebRTC chapterWebSocket Cheat Sheetapprtc.appspot.com 的信令是通過(guò)Google App Engine Channel API完成的娶吞,Google App Engine Channel API是使用了Comet技術(shù)(長(zhǎng)時(shí)間輪詢(xún))讓APP后端和web客戶(hù)端 實(shí)現(xiàn)推送通訊功能垒迂。這里有個(gè)代碼預(yù)演械姻。另外一種方案,可以通過(guò)Ajax去輪詢(xún)服務(wù)端獲取signaling机断,但會(huì)導(dǎo)致一堆多余的網(wǎng)絡(luò)請(qǐng)求楷拳,特別是在移動(dòng)客戶(hù)端。在一個(gè)會(huì)話(huà)被確定后吏奸,用戶(hù)仍然需要去輪詢(xún)signaling信息欢揖,因?yàn)闀?huì)話(huà)可能會(huì)被其他用戶(hù)改變或者終止》芪担《WebRTC》這本書(shū)就用了這種經(jīng)過(guò)優(yōu)化輪詢(xún)頻率的方法她混。信令壓縮雖然一個(gè)信令服務(wù)器在每一個(gè)客戶(hù)端中花費(fèi)相當(dāng)小的帶寬和CPU,但是一個(gè)普遍使用的應(yīng)用可能需要從不同的地點(diǎn)處理很多信息泊碑,并且有很多高的并發(fā)數(shù)坤按。一個(gè)大流量的WebRTC 應(yīng)用需要心理服務(wù)端去處理相當(dāng)大的負(fù)荷。這里我們不講細(xì)節(jié)馒过,下面有一些 處理高數(shù)據(jù)量臭脓,高性能的信息通訊設(shè)置:1.XMPP,最初被稱(chēng)為Jabber:一種被開(kāi)發(fā)用來(lái)即時(shí)通訊的協(xié)議腹忽,可以用來(lái)做signaling来累。服務(wù)端可以用 ejabberd andOpenfire實(shí)現(xiàn)。JavaScript客戶(hù)端窘奏,例如 Strophe.js 使用BOSH去模仿雙向通訊流嘹锁,但因?yàn)?a target="_blank" rel="nofollow">各種原因,BOSH可能不像WebSocket那么有效率着裹。(Jingle 是一種支持視頻和語(yǔ)音的XMPP擴(kuò)展兼耀,WebRTC從libjingle庫(kù)(Jingle的C++實(shí)現(xiàn)庫(kù))里使用了網(wǎng)絡(luò)和傳輸組件 )2.像 ZeroMQ(據(jù)說(shuō)TokBox服務(wù)端使用了)、OpenMQ的開(kāi)源庫(kù)求冷。3.使用支持WebSocket商業(yè)的云服務(wù)平臺(tái)瘤运。4.商業(yè)的WebRTC 平臺(tái),比如vLine.開(kāi)發(fā)者Phil Leggetter提供了一系列信息服務(wù)器和第三方庫(kù)列表在Real-Time Web Technologies Guide匠题。用Node開(kāi)發(fā)基于Sockket.io的信令服務(wù)下面有個(gè)例子,Socket.io可以輕易創(chuàng)建一個(gè)用于交換信息的服務(wù)拯坟。Socket.io非常適合WebRTC 的信令,因?yàn)樗褪且浴皉ooms”的概念設(shè)計(jì)的韭山。這個(gè)demo不是一個(gè)產(chǎn)品級(jí)別的服務(wù)郁季,但是能夠應(yīng)付小數(shù)量的用戶(hù)冷溃。Socket.io通過(guò)下面的回調(diào)使用WebSocket: Adobe Flash Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe and JSONP polling。Socket.io也被移植到后端版本梦裂,但是最廣為人知的是Node版本似枕。這個(gè)demo沒(méi)有WebRTC,它只是展示怎么創(chuàng)建一個(gè)webapp的signaling。用控制臺(tái)查看log年柠,去看下客戶(hù)端加入一個(gè)房間和交換數(shù)據(jù)發(fā)生了什么變化凿歼。WebRTC codelab會(huì)一步一步教你怎么整合這個(gè)demo變成一個(gè)完整的WEbRTC視頻聊天應(yīng)用。你可以從step 5 of the codelab repo下載源碼或者在samdutton-nodertc.jit.su運(yùn)行(用兩個(gè)瀏覽器打開(kāi)這個(gè)鏈接 )這是客戶(hù)端的index.htl:

還有JavaScript文件main.js:
完整的服務(wù)端:
var static = require('node-static');
var http = require('http');
var file = new(static.Server)();
var app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);

var io = require('socket.io').listen(app);

io.sockets.on('connection', function (socket){

// convenience function to log server messages to the client
function log(){
var array = ['>>> Message from server: '];
for (var i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}

socket.on('message', function (message) {
log('Got message:', message);
// for a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});

socket.on('create or join', function (room) {
var numClients = io.sockets.clients(room).length;

log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);

if (numClients === 0){
  socket.join(room);
  socket.emit('created', room);
} else if (numClients === 1) {
  io.sockets.in(room).emit('join', room);
  socket.join(room);
  socket.emit('joined', room);
} else { // max two clients
  socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);

});

});

復(fù)制代碼
要運(yùn)行這個(gè)app冗恨,你需要安裝Node, socket.io and node-static答憔。可以在 nodejs.org下載Node掀抹,再安裝 socket.io 和node-static虐拓,在終端運(yùn)行Node Package Manager:npm install socket.ionpm install node-static啟動(dòng)服務(wù),運(yùn)行下面命令node server.js在瀏覽器打開(kāi) localhost:2013.用新的瀏覽器打開(kāi)localhost:2013 傲武,用控制臺(tái)看下發(fā)生了什么使用 RTCDataChannel交換信息初始化一個(gè)WebRTC會(huì)話(huà)蓉驹,必須有一個(gè)信令 服務(wù)器。然而揪利,一旦兩端確定了 一個(gè)通話(huà)态兴,理論上,RTCDataChannel可以接替信令通道土童,這可以減少信號(hào)的延遲诗茎。一旦信息直接在兩端通訊,RTCDataChannel會(huì)幫忙減少帶寬使用和進(jìn)程開(kāi)銷(xiāo)献汗。沒(méi)有例子敢订,但可以看下面:信令性能和擴(kuò)展性1.RTCPeerConnection 不會(huì)搜集candidates,直到setLocalDescription() 被調(diào)用罢吃。這個(gè)被JSEP IETF draft.強(qiáng)制要求了楚午。2.利用Trickle ICE(看上面解釋?zhuān)航邮盏絚andidates后立即調(diào)用addIceCandidate(),現(xiàn)成的信令服務(wù) 這里有一些可以用的WebRTC signaling服務(wù)端:
webRTC.io: 第一個(gè)抽象庫(kù) for WebRTC.
easyRTC: 一個(gè)完整的WebRTC包 a full-stack WebRTC package.
Signalmaster:一個(gè)使用 SimpleWebRTCJavaScrip客戶(hù)端庫(kù)的signaling服務(wù)

如果你一點(diǎn)都不想編碼尿招,你可以用完整的商業(yè)WebRTC平臺(tái)矾柜,像vLine, OpenTok and Asterisk愛(ài)立信創(chuàng)建了一個(gè) signaling server using PHP on Apache,在WebRTC早期的時(shí)候就谜,現(xiàn)在這個(gè)已經(jīng)被棄用了怪蔑,但是如果你考慮到相似的情況,這個(gè)代碼還是值得一看的丧荐。六.Signaling安全
Security is the art of making nothing happen.
Salman Rushdie

所有WebRTC 組件都被強(qiáng)制加密缆瓣。
但是信令機(jī)制沒(méi)有被WebRTC標(biāo)準(zhǔn)定義,所有確保信令安全取決于你虹统,如果一個(gè)攻擊者想去劫持信令弓坞,他們會(huì)導(dǎo)致會(huì)話(huà)中止隧甚,重定向鏈接和記錄渡冻,改變或者注入內(nèi)容宜雀。

一個(gè)牢固的信令最重要的功能是使用加密協(xié)議禀综,HTTPS 和WSS (i.e TLS)可以確保信息不會(huì)非加密攔截届氢。
同時(shí)寂祥,小心不要廣播信令信息惜犀,不然攻擊者可以使用相同的信令服務(wù)鏈接其他來(lái)電用戶(hù)刹缝。

使用 TLS.去確保WebRTC應(yīng)用的安全颂砸。
信令交互完之后碰缔,使用ICE去處理NATs和防火墻對(duì)于元數(shù)據(jù)的信令戳护,WebRTC應(yīng)用可以使用中間服務(wù)金抡,但實(shí)際的媒體和數(shù)據(jù)流在一個(gè)會(huì)話(huà)確立后,RTCPeerConnection 嘗試去直連客戶(hù)端:P2P在一個(gè)簡(jiǎn)單的世界里腌且,每一個(gè)WebRTC端都有一個(gè)唯一的地址梗肝,這樣他可以與其他端交換數(shù)據(jù),以便直接 通訊铺董。

實(shí)際情況下巫击,大多數(shù)設(shè)備都在一個(gè)或多個(gè)NAT層后面,有些有防毒軟件阻礙確定的端口和協(xié)議精续,還有很多在代理和公司的防火墻后面坝锰。防火墻和NAT實(shí)際上可能由一些類(lèi)似家庭wifi路由器產(chǎn)生的。
WebRTC 可以使用ICE框架去克服真實(shí)世界的復(fù)雜網(wǎng)絡(luò)驻右。為了實(shí)現(xiàn)這個(gè)功能什黑,你的應(yīng)用必須傳ICE服務(wù)地址給RTCPeerConnection,如下所述堪夭。ICE 試著尋找最佳路線(xiàn)去連接對(duì)方愕把,它會(huì)并行的尋找所有可能性,然后選擇最有效的可行方式森爽。 ICE首先會(huì)嘗試用設(shè)備系統(tǒng)或網(wǎng)卡獲取到的主機(jī)地址去建立連接恨豁;如果這個(gè)失敗了(設(shè)備在NATs后面就會(huì))ICE從STUN服務(wù)器獲得外部的地址,如果這個(gè)也失敗了爬迟,就用TURN中轉(zhuǎn)服務(wù)器做通訊橘蜜。
也就是說(shuō):
STUN服務(wù)器用來(lái)獲取外部網(wǎng)絡(luò)地址。
如果P2P失敗的話(huà),[size=14.4444446563721px]TURN服務(wù)器用來(lái)中繼通訊计福。

每一個(gè)TURN服務(wù)器都支持STUN:一個(gè)TURN服務(wù)器是由一個(gè)STUN服務(wù)器加上中繼功能跌捆。ICE也可以用來(lái)應(yīng)付復(fù)雜的NAT設(shè)置:
事實(shí)上,NAT的”打洞“可能需要除了公共IP之外的端口地址象颖。

WebRTC應(yīng)用在iceServers配置對(duì)象(RTCPeerConnection constructor)里設(shè)置STUN and/or TURN服務(wù)器地址佩厚。
apprtc.appspot.com里這個(gè)值像這樣:
{
'iceServers': [
{
'url': 'stun:stun.l.google.com:19302'
},
{
'url': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'url': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}

復(fù)制代碼

一旦RTCPeerConnection 有了這些信息,ICE會(huì)自動(dòng)啟動(dòng):RTCPeerConnection 使用ICE框架計(jì)算出兩端間最佳路線(xiàn)说订,需要STUN和TURN服務(wù)器抄瓦。

STUNNATs會(huì)給它的設(shè)備提供一個(gè)內(nèi)部網(wǎng)絡(luò)IP地址,但這個(gè)地址不能在外網(wǎng)使用陶冷,因?yàn)闆](méi)有外網(wǎng)的地址钙姊,所有WebRTC沒(méi)辦法做連接,為解決這個(gè)問(wèn)題埂伦,WebRTC使用了STUN煞额。STUN服務(wù)架設(shè)在外網(wǎng),它有一個(gè)簡(jiǎn)單的任務(wù):獲取一個(gè)發(fā)送請(qǐng)求的設(shè)備(運(yùn)行在NAT后邊的應(yīng)用)的IP和端口赤屋,然后返回這個(gè)地址立镶。換句話(huà)說(shuō)壁袄,應(yīng)用使用STUN服務(wù)器發(fā)現(xiàn)它的外網(wǎng)IP和端口类早,這個(gè)過(guò)程確保了一個(gè)WebRTC端獲得它自己的公共地址,然后通過(guò)signaling機(jī)制發(fā)送這個(gè)信息給另一端嗜逻,這樣就可以建立起一個(gè)直接連接涩僻。(在實(shí)際中,不同的NATs有不同的工作方式栈顷,可能有多個(gè)NAT層逆日,但是原理是一樣的)STUN服務(wù)器不需要做太多工作和存儲(chǔ)太多東西,所以簡(jiǎn)單的STUN服務(wù)器可以應(yīng)付大量的請(qǐng)求萄凤。根據(jù) webrtcstats.com的統(tǒng)計(jì)室抽,使用STUN方式建立WebRTC通話(huà)的成功率有86%的。

TURNRTCPeerConnection 會(huì)試著用UDP在兩端建立一個(gè)直連靡努,如果失敗了坪圾,RTCPeerConnection 會(huì)改用TCP,如果這個(gè)再失敗了惑朦,TURN服務(wù)器會(huì)被作為后備方案使用兽泄,在兩端間中繼數(shù)據(jù)。
重述:TURN是在兩端間中轉(zhuǎn)視頻/語(yǔ)音/數(shù)據(jù) 流漾月,而不是發(fā)送數(shù)據(jù)病梢。

TURN 有個(gè)公共地址,所以每個(gè)端即使在防火墻或者代理后面梁肿,也能訪(fǎng)問(wèn)到蜓陌。
TURN有個(gè)簡(jiǎn)單的任務(wù)觅彰,中轉(zhuǎn)數(shù)據(jù)流,但不像STUN钮热,TURN會(huì)花費(fèi)大量帶寬缔莲。所有,TURN需要夠強(qiáng)壯霉旗。


圖表表示TURN的作用:?jiǎn)渭兊腟TUN不起作用痴奏,客戶(hù)端就會(huì)轉(zhuǎn)向使用TURN。
部署 STUN 和 TURN 服務(wù)器作為測(cè)試厌秒,谷歌公布了一個(gè)公共的STUN服務(wù)读拆,stun.l.google.com:19302栓袖, apprtc.appspot.com用的就是這個(gè)胁勺。作為一個(gè)產(chǎn)品級(jí)別的 STUN/TURN服務(wù)器,我們建議使用 rfc5766-turn-server逗威,STUN 和TURN的源碼可以從code.google.com/p/rfc5766-turn-server獲取蚌讼,這個(gè)鏈接也包括了部署的資料辟灰。A VM image for Amazon Web Services is also available.本社區(qū)也發(fā)布了部署教程:部署教程一個(gè)可代替的TURN服務(wù)器是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 makesudo 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_PATHCopy restund.conf to /etc/restund.confSet restund.conf to use the right 10. IP address
Run restund
Test using stund client from remote machine: ./client IP:port

七.突破p2p:多人會(huì)議WebRTC你可以需要看下Justin Uberti提議的IETF標(biāo)識(shí):請(qǐng)求TURN服務(wù)的API很容易想象到一些場(chǎng)景不只是一對(duì)一的視頻通話(huà)芥喇,舉個(gè)例子,公司小組需要一個(gè)視頻會(huì)議凰萨,或者一個(gè)公開(kāi)的演講继控,一個(gè)演講者面對(duì)數(shù)百(或者數(shù)千)的觀看者。一個(gè)WebRTC應(yīng)用可以使用多個(gè)RTCPeerConnections胖眷,這樣每一個(gè)端可以連接其他端形成一個(gè)網(wǎng)絡(luò)武通。talky.io就是使用這種方法實(shí)現(xiàn),對(duì)于少數(shù)的用戶(hù)珊搀,可以很好的工作冶忱。但是進(jìn)程和帶寬開(kāi)銷(xiāo)會(huì)非常大,特別是移動(dòng)客戶(hù)端境析。

在一個(gè)星型結(jié)構(gòu)里囚枪,一個(gè)WebRTC客戶(hù)端可以選擇一個(gè)端去分布數(shù)據(jù)流給所有的用戶(hù),你可以自己設(shè)計(jì)重新分配機(jī)制的服務(wù)和構(gòu)造區(qū)實(shí)現(xiàn)這種方式(werrtc.org提供了一個(gè)樣例sample client application)從Chrome31和Opera18 開(kāi)始簿晓,從一個(gè)RTCPeerConnection 獲取的媒體流眶拉,可以作為對(duì)方的輸入:這里有個(gè)demosimpl.info/multi。這樣可以確保更靈活的結(jié)構(gòu)憔儿,因?yàn)樗梢栽试Sweb應(yīng)用通過(guò)選擇哪個(gè)用戶(hù)可以連接去控制一個(gè)通話(huà) 路由忆植。多點(diǎn)控制部件MCU(Multipoint Control Unit)
大量用戶(hù)通話(huà)的更好解決方案是使用Multipoint Control Unit(MCU)。這是一個(gè)在大量參與者間分布媒體的橋接服務(wù)器。MCUs可以在一個(gè)視頻 會(huì)議里處理不同的分辨率朝刊,編解碼耀里,和幀速率。對(duì)于多端會(huì)議拾氓,有很多因素要考慮:最重要的是冯挎,從多個(gè)源里,怎么顯示多個(gè)視頻和混合音頻咙鞍。像 vLine 的云平臺(tái)也在致力于優(yōu)化傳輸路由房官。

你可以去買(mǎi)一個(gè)MCU硬件或者自己搭一個(gè)。

Cisco MCU背部有幾個(gè)開(kāi)源的MCU硬件款可以選续滋,例如 Licode(以前稱(chēng)為L(zhǎng)ynckia) 生產(chǎn)的開(kāi)源 MCU for WebRTC; OpenTok 平臺(tái)的Mantis.突破瀏覽器: VoIP, telephones 和 messagingWebRTC 的標(biāo)準(zhǔn)讓瀏覽器和不同設(shè)備不同平臺(tái)翰守,例如手機(jī)或者一個(gè)視頻會(huì)議系統(tǒng),進(jìn)行通話(huà)稱(chēng)為可能疲酌。SIP是一種信令協(xié)議蜡峰,用來(lái)做VoIP和視頻系統(tǒng)。為了讓W(xué)ebRTC和SIP端通訊朗恳,WebRTC需要一個(gè)代理服務(wù)器去調(diào)解信令湿颅。信令一定會(huì)經(jīng)過(guò)網(wǎng)關(guān),但是一旦會(huì)話(huà)建立粥诫,視頻和語(yǔ)音就能在兩端傳輸油航。PSTN,公共電話(huà)交換網(wǎng)絡(luò)臀脏,是舊式模擬電話(huà)的交換網(wǎng)絡(luò)劝堪。為了WebRTC和電話(huà)進(jìn)行通話(huà),必須通過(guò)一個(gè)PSTN網(wǎng)關(guān)揉稚。同理,要讓W(xué)ebRTC跟Jingle端(像IM客戶(hù)端)通訊熬粗,需要一個(gè)中間XMPP服務(wù)器搀玖。Jingle作為XMPP的擴(kuò)展,用來(lái)實(shí)現(xiàn)視頻和語(yǔ)音能夠作為信息服務(wù):現(xiàn)在的WebRTC就是基于C++實(shí)現(xiàn)libjingle 庫(kù)發(fā)展來(lái)的驻呐,Jingle最初是Google Talk的技術(shù)灌诅。一堆應(yīng)用庫(kù),平臺(tái)讓W(xué)ebRTC能在實(shí)際中通訊:
sipML5:一個(gè) 開(kāi)源的 JavaScript SIP 客戶(hù)端
jsSIP: JavaScript SIP庫(kù)
Phono: 開(kāi)源JavaScript phone API, 作為一個(gè)插件
Zingaya: 一個(gè)嵌入式電話(huà)部件
Twilio: 音頻和信息
Uberconference: 會(huì)議技術(shù)

sipML5 的開(kāi)發(fā)者也開(kāi)發(fā)了webrtc2sip的網(wǎng)關(guān)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! 發(fā)現(xiàn)更多WebRTC codelab: 一步一步教你怎么打造一個(gè)視頻和文本聊天應(yīng)用含末,使用Socket.io Signaling服務(wù)猜拾。在Node上面跑。2013 Google I/O WebRTC presentation WebRTC領(lǐng)頭人Justin Uberti.
Chris Wilson 關(guān)于WebRTC的介紹:Introduction to WebRTC Apps.
WebRTC Book 這本書(shū)有很多關(guān)于數(shù)據(jù)佣盒、signaling挎袜、網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)的細(xì)節(jié)。
WebRTC and Signaling: What Two Years Has Taught Us:介紹為什么把signaling脫離出標(biāo)準(zhǔn)是個(gè)好主意
A Practical Guide to Building WebRTC Apps:提供很多WebRTC的技術(shù)和基礎(chǔ)建設(shè)信息。
WebRTC chapter 深入研究WebRTC的結(jié)構(gòu)盯仪,使用案例和性能紊搪。翻譯?http://www.html5rocks.com/en/tutorials/webrtc/infrastructure/
!WebRTC中文社區(qū)原創(chuàng)全景,轉(zhuǎn)載請(qǐng)注明出處耀石,謝謝,水平有限,部分意思可能不到位爸黄,建議參考英文原帖滞伟!
WebRTC 可以p2p視頻通話(huà)
但是...
WebRTC 仍然需要幾個(gè)服務(wù):
信令服務(wù): 使客戶(hù)端之間交換數(shù)據(jù)用來(lái)協(xié)調(diào)建立通話(huà)
NAT穿透服務(wù):應(yīng)付NATs和防火墻

這篇文章會(huì)教你怎么搭建信令服務(wù),和用STUN/TURN服務(wù)去做nat穿透炕贵。另外诗良,我們會(huì)解釋W(xué)ebRTC是怎么做到多端通話(huà)的。以及如何和VoIP/PSTN(電話(huà))建立通話(huà)鲁驶。
如果你對(duì)WebRTC還沒(méi)有基礎(chǔ)鉴裹,我們強(qiáng)烈建議你先看下Getting Started With WebRTC
一.什么是信令服務(wù)(Signaling)钥弯?信令是一個(gè)協(xié)調(diào)溝通的過(guò)程径荔,為了讓一個(gè)WebRTC應(yīng)用發(fā)起一個(gè)“通話(huà)”,客戶(hù)端間需要交換以下信令信息:1.發(fā)起和關(guān)閉一個(gè)通話(huà)的控制信息脆霎;2.錯(cuò)誤信息总处;3.媒體元數(shù)據(jù),比如編碼解碼設(shè)置睛蛛,帶寬和媒體類(lèi)型鹦马;4.Key數(shù)據(jù),用于確保安全通訊忆肾;5.網(wǎng)絡(luò)數(shù)據(jù)荸频,比如主機(jī)在外網(wǎng)下的IP地址和端口】透裕客戶(hù)端的信令處理需要一種來(lái)回傳遞信息的方法旭从,這種機(jī)制沒(méi)有被WebRTC定義,你需要自己去創(chuàng)建它场仲。下面我們將描繪幾種構(gòu)建信令服務(wù)的方法和悦。在此之前,先講幾個(gè)概念……為什么WebRTC沒(méi)有定義信令渠缕?為了避免冗余和最大化兼容已經(jīng)確立的技術(shù)鸽素,WebRTC沒(méi)有指定信令的方法和協(xié)議。-------------------------------(WebRTC設(shè)計(jì)思想是完全指定和控制媒體層亦鳞,但是讓signaling層盡量脫離應(yīng)用馍忽,原因是不同的應(yīng)用可能會(huì)使用不同的協(xié)議棒坏,比如已經(jīng)存在的SIP或者Jingle呼叫協(xié)議等。這份協(xié)議中舵匾,需要交換的關(guān)鍵信息是多媒體會(huì)議的描述信息俊抵,包括在媒體層確定必要的傳輸方式和 媒體配置信息)------------------------------------------------JSEP的結(jié)構(gòu)同樣避免了讓瀏覽器保存狀態(tài)信息,如果讓瀏覽器成為一個(gè)保存信令狀態(tài)的機(jī)器坐梯,會(huì)出現(xiàn)一個(gè)問(wèn)題徽诲,就是每次當(dāng)頁(yè)面重載的時(shí)候,信令會(huì)丟失吵血。所以更好的方案是用服務(wù)器保存信令狀態(tài)谎替。

JSEP協(xié)議要求端對(duì)端之間需要發(fā)起(offer)和回應(yīng)(answer)上面提到的數(shù)據(jù)。offer和answer用SDP(Session Description Protocol format信令描述協(xié)議格式)交流蹋辅,像這樣:
如果想知道所有的SDP代表的意思钱贯,可以看下這個(gè)鏈接:IEFT examples記住,WebRTC這樣設(shè)計(jì)是為了讓offer端和answer端能夠在tweaked之前通過(guò)SDP文檔設(shè)置好參數(shù)侦另。舉個(gè)例子: apprtc.appspot.com 里的preferAudioCodec()方法用來(lái)設(shè)置默認(rèn)的編解碼方式和比特率秩命,SDP用JavaScript比較難操作,未來(lái)的版本可能會(huì)用JSON代替褒傅,但是SDP還是有一些優(yōu)勢(shì)的弃锐。二.RTCPeerConnection + 信令: offer,answer和candidateRTCPeerConnection 是WebRTC客戶(hù)端在兩端建立音視頻通訊的API。初始化RTCPeerConnection進(jìn)程需要兩個(gè)步驟:1.確定當(dāng)期的媒體條件殿托,例如分辨率霹菊,編解碼能力。這些是給offer和answer的原始數(shù)據(jù)支竹。2.獲得應(yīng)用主機(jī)的網(wǎng)絡(luò)地址(也就是candidate)一旦這些本地?cái)?shù)據(jù)被確定好了旋廷,就必須通過(guò)信令機(jī)制在不同端交換。假設(shè)A想呼叫B礼搁,下面是整個(gè)offer/answer機(jī)制的細(xì)節(jié):1.A創(chuàng)建一個(gè)RTCPeerConnection對(duì)象饶碘。2.A用RTCPeerConnection的createOffer()方法創(chuàng)建一個(gè)offer(用SDP協(xié)議描述)。3.A用他的offer設(shè)置本地描述setLocalDescription()叹坦。4.A序列化offer熊镣,并且用信令機(jī)制發(fā)送給B.5.B用A的offer調(diào)用setRemoteDescription()設(shè)置對(duì)方的描述,B的RTCPeerConnection就知道了A的配置了募书。6.B調(diào)用createAnswer(),如果成功會(huì)返回一個(gè)本地的session描述测蹲,既B的answer莹捡。7.B用她的answer設(shè)置為本地的描述,通過(guò)調(diào)用setLocalDescription().設(shè)置本地描述8.B用信令機(jī)制發(fā)送序列化后的answer給A扣甲。9.A設(shè)置B的answer為對(duì)方session描述篮赢,通過(guò)調(diào)用setRemoteDescription()設(shè)置對(duì)方的描述.(至此齿椅,A和B都設(shè)置了本地和對(duì)方的描述)A和B還需要交換網(wǎng)絡(luò)信息。'finding candidates' 指的是用ICE framework.去發(fā)現(xiàn)網(wǎng)絡(luò)接口和端口启泣。1.A用一個(gè)onIceCandidate handler創(chuàng)建一個(gè)RTCPeerConnection對(duì)象涣脚。2.當(dāng)網(wǎng)絡(luò)candidates有效時(shí)這個(gè)handler會(huì)被調(diào)用。3.在這個(gè)handler里寥茫,A發(fā)送序列化的candidates數(shù)據(jù)給B遣蚀,通過(guò)信令通道。4.當(dāng)B從A獲得一個(gè)candidate信息纱耻,她調(diào)用addIceCandidate()去給對(duì)方描述添加candidate芭梯。JSEP支持ICE Candidate Trickling技術(shù)(允許呼叫者在首次初始化offer后,逐次發(fā)送candidates給被呼叫者弄喘,這是為了讓被呼叫者開(kāi)始設(shè)置連接而不用等到全部的candidates到達(dá))WebRTC 的信令編碼下面是W3C code exampleW3C代碼樣例玖喘,概況了完整的signaling過(guò)程。樣例假設(shè)已經(jīng)有了信令機(jī)制:SignalingChannel蘑志。Signaling 會(huì)在下面探討比較多的細(xì)節(jié)累奈。var signalingChannel = new SignalingChannel();
var configuration = {
'iceServers': [{
'url': 'stun:stun.example.org'
}]
};
var pc;

// call start() to initiate

function start() {
pc = new RTCPeerConnection(configuration);

// send any ice candidates to the other peer
pc.onicecandidate = function (evt) {
if (evt.candidate)
signalingChannel.send(JSON.stringify({
'candidate': evt.candidate
}));
};

// let the 'negotiationneeded' event trigger offer generation
pc.onnegotiationneeded = function () {
pc.createOffer(localDescCreated, logError);
}

// once remote stream arrives, show it in the remote video element
pc.onaddstream = function (evt) {
remoteView.src = URL.createObjectURL(evt.stream);
};

// get a local stream, show it in a self-view and add it to be sent
navigator.getUserMedia({
'audio': true,
'video': true
}, function (stream) {
selfView.src = URL.createObjectURL(stream);
pc.addStream(stream);
}, logError);
}

function localDescCreated(desc) {
pc.setLocalDescription(desc, function () {
signalingChannel.send(JSON.stringify({
'sdp': pc.localDescription
}));
}, logError);
}

signalingChannel.onmessage = function (evt) {
if (!pc)
start();

var message = JSON.parse(evt.data);
if (message.sdp)
pc.setRemoteDescription(new RTCSessionDescription(message.sdp), function () {
// if we received an offer, we need to answer
if (pc.remoteDescription.type == 'offer')
pc.createAnswer(localDescCreated, logError);
}, logError);
else
pc.addIceCandidate(new RTCIceCandidate(message.candidate));
};

function logError(error) {
log(error.name + ': ' + error.message);
}

復(fù)制代碼
查看“單頁(yè)面”視頻聊天的例子simpl.info/pc.可以在 控制臺(tái)的lgo看到offer/answer 和candidate 的交換過(guò)程。如果你想了解更多急但,可以在Chrome瀏覽器打開(kāi) chrome://webrtc-internals 或在opera打開(kāi) opera://webrtc-internals下載完整的代碼澎媒。三.成員發(fā)現(xiàn)機(jī)制(Peer discovery)這里有個(gè)問(wèn)題: 我怎么發(fā)現(xiàn)誰(shuí)可以通話(huà)?對(duì)于電話(huà)羊始,我們有電話(huà)號(hào)碼和目錄旱幼。對(duì)于在線(xiàn)視頻聊天,我們需要身份和業(yè)務(wù)管理系統(tǒng)和一種讓用戶(hù)開(kāi)始會(huì)話(huà)的手段突委。WebRTC apps需要一種 讓客戶(hù)端標(biāo)示自己以便可以開(kāi)始和加入會(huì)話(huà)的方法柏卤。成員發(fā)現(xiàn)機(jī)制Peer discovery mechanisms沒(méi)有被WebRTC定義,在這里我們不用做選擇匀油。這個(gè)過(guò)程可以像發(fā)送一個(gè)URL地址這么簡(jiǎn)單缘缚,對(duì)于視頻聊天應(yīng)用,比如 talky.io, tawk.com and browsermeeting.com敌蚜,你通過(guò)分享一個(gè)通用鏈接邀請(qǐng)別人進(jìn)入一個(gè)會(huì)話(huà)桥滨。開(kāi)發(fā)者Chris Ball開(kāi)發(fā)了一個(gè)有趣的實(shí)驗(yàn):serverless-webrtc,可以讓W(xué)ebRTC呼叫參與者分享元數(shù)據(jù)弛车,通過(guò)任何信息服務(wù)齐媒,比如IM,email或者信鴿纷跛。四.怎么創(chuàng)建一個(gè)signling服務(wù)喻括?再說(shuō)一遍:信令機(jī)制沒(méi)有被WebRTC標(biāo)準(zhǔn)定義,無(wú)論你選擇哪種 贫奠,你需要一個(gè)中間服務(wù)器去交換信令信息和不同客戶(hù)端間的應(yīng)用數(shù)據(jù)唬血。慶幸的是望蜡,信令信息很小,大部分交換都是在通話(huà)開(kāi)始的時(shí)候拷恨。在測(cè)試 apprtc.appspot.comsamdutton-nodertc.jit.su 時(shí)脖律,我們發(fā)現(xiàn)一個(gè) 視頻會(huì)話(huà),總共有大概30-45的信息被信令服務(wù)器處理腕侄,信息大小大概是10kB小泉。除了相對(duì)要求不高的帶寬,WebRTC 信令服務(wù)器不用花費(fèi)過(guò)多的內(nèi)存和進(jìn)程兜挨,因?yàn)橹恍枰D(zhuǎn)發(fā)信息和保持很少的會(huì)議狀態(tài)數(shù)據(jù)(比如那個(gè)客戶(hù)端被連接了)小貼士 :信令機(jī)制不僅可以用來(lái)交換會(huì)話(huà)元數(shù)據(jù)膏孟,也能用來(lái)傳達(dá)應(yīng)用數(shù)據(jù)。它就是個(gè)信息服務(wù)拌汇。五.從服務(wù)端推信息給客戶(hù)端一個(gè)信令服務(wù)器需要是雙向的:客戶(hù)端到服務(wù)器和服務(wù)器到客戶(hù)端柒桑。雙向通訊違反了HTTP 客戶(hù)端/服務(wù)端 請(qǐng)求/回復(fù)的模式,但是有一些發(fā)展多年的技術(shù)噪舀,例如long polling(長(zhǎng)時(shí)間輪詢(xún)) 被用來(lái)從服務(wù)端發(fā)送數(shù)據(jù)給一個(gè)運(yùn)行中的web應(yīng)用魁淳。最近,EventSource API 被廣泛的應(yīng)用与倡,它允許“服務(wù)端發(fā)送事件”:數(shù)據(jù)通過(guò)HTTP從服務(wù)端發(fā)送給瀏覽器界逛。這里有個(gè)簡(jiǎn)單的demo:simpl.info/es。EventSource被設(shè)計(jì)為一種消息傳送方式纺座,但是它可以跟XHR 結(jié)合做成一個(gè)交換signaling的服務(wù):從一個(gè)呼叫者傳遞信息息拜,由XHR 請(qǐng)求傳遞,推送給被呼叫者净响。WebSocket 是一種更自然的解放方案少欺,它是為了全雙工 客戶(hù)端-服務(wù)端通訊設(shè)計(jì)的(信息可以在同一時(shí)間在兩個(gè)端傳遞)。用純WebSocket或者Server-Sent Events (EventSource) 做為signaling服務(wù)的優(yōu)點(diǎn)是后端調(diào)用這些APIs可以用多種Web框架實(shí)現(xiàn)馋贤,在使用PHP,Python和Ruby的情況下赞别。大約有四分之三的瀏覽器支持WebSocket ,更重要的是,所有支持WebRTC的桌面瀏覽器和移動(dòng)瀏覽器都支持WebSocket配乓。TLS(安全傳輸層協(xié)議)應(yīng)該用于所有的鏈接仿滔,已確保信息不會(huì)被截?cái)唷M瑫r(shí)用proxy traversal減少問(wèn)題(更多關(guān)于WebSocket 和proxy traversal的資料可以看WebRTC chapterWebSocket Cheat Sheetapprtc.appspot.com 的信令是通過(guò)Google App Engine Channel API完成的犹芹,Google App Engine Channel API是使用了Comet技術(shù)(長(zhǎng)時(shí)間輪詢(xún))讓APP后端和web客戶(hù)端 實(shí)現(xiàn)推送通訊功能崎页。這里有個(gè)代碼預(yù)演。另外一種方案腰埂,可以通過(guò)Ajax去輪詢(xún)服務(wù)端獲取signaling实昨,但會(huì)導(dǎo)致一堆多余的網(wǎng)絡(luò)請(qǐng)求,特別是在移動(dòng)客戶(hù)端盐固。在一個(gè)會(huì)話(huà)被確定后荒给,用戶(hù)仍然需要去輪詢(xún)signaling信息,因?yàn)闀?huì)話(huà)可能會(huì)被其他用戶(hù)改變或者終止刁卜≈镜纾《WebRTC》這本書(shū)就用了這種經(jīng)過(guò)優(yōu)化輪詢(xún)頻率的方法。信令壓縮雖然一個(gè)信令服務(wù)器在每一個(gè)客戶(hù)端中花費(fèi)相當(dāng)小的帶寬和CPU蛔趴,但是一個(gè)普遍使用的應(yīng)用可能需要從不同的地點(diǎn)處理很多信息挑辆,并且有很多高的并發(fā)數(shù)。一個(gè)大流量的WebRTC 應(yīng)用需要心理服務(wù)端去處理相當(dāng)大的負(fù)荷孝情。這里我們不講細(xì)節(jié)鱼蝉,下面有一些 處理高數(shù)據(jù)量,高性能的信息通訊設(shè)置:1.XMPP箫荡,最初被稱(chēng)為Jabber:一種被開(kāi)發(fā)用來(lái)即時(shí)通訊的協(xié)議魁亦,可以用來(lái)做signaling。服務(wù)端可以用 ejabberd andOpenfire實(shí)現(xiàn)羔挡。JavaScript客戶(hù)端洁奈,例如 Strophe.js 使用BOSH去模仿雙向通訊流,但因?yàn)?a target="_blank" rel="nofollow">各種原因绞灼,BOSH可能不像WebSocket那么有效率利术。(Jingle 是一種支持視頻和語(yǔ)音的XMPP擴(kuò)展,WebRTC從libjingle庫(kù)(Jingle的C++實(shí)現(xiàn)庫(kù))里使用了網(wǎng)絡(luò)和傳輸組件 )2.像 ZeroMQ(據(jù)說(shuō)TokBox服務(wù)端使用了)低矮、OpenMQ的開(kāi)源庫(kù)印叁。3.使用支持WebSocket商業(yè)的云服務(wù)平臺(tái)。4.商業(yè)的WebRTC 平臺(tái)军掂,比如vLine.開(kāi)發(fā)者Phil Leggetter提供了一系列信息服務(wù)器和第三方庫(kù)列表在Real-Time Web Technologies Guide轮蜕。用Node開(kāi)發(fā)基于Sockket.io的信令服務(wù)下面有個(gè)例子,Socket.io可以輕易創(chuàng)建一個(gè)用于交換信息的服務(wù)。Socket.io非常適合WebRTC 的信令良姆,因?yàn)樗褪且浴皉ooms”的概念設(shè)計(jì)的肠虽。這個(gè)demo不是一個(gè)產(chǎn)品級(jí)別的服務(wù),但是能夠應(yīng)付小數(shù)量的用戶(hù)玛追。Socket.io通過(guò)下面的回調(diào)使用WebSocket: Adobe Flash Socket, AJAX long polling, AJAX multipart streaming, Forever Iframe and JSONP polling税课。Socket.io也被移植到后端版本,但是最廣為人知的是Node版本痊剖。這個(gè)demo沒(méi)有WebRTC,它只是展示怎么創(chuàng)建一個(gè)webapp的signaling韩玩。用控制臺(tái)查看log,去看下客戶(hù)端加入一個(gè)房間和交換數(shù)據(jù)發(fā)生了什么變化陆馁。WebRTC codelab會(huì)一步一步教你怎么整合這個(gè)demo變成一個(gè)完整的WEbRTC視頻聊天應(yīng)用找颓。你可以從step 5 of the codelab repo下載源碼或者在samdutton-nodertc.jit.su運(yùn)行(用兩個(gè)瀏覽器打開(kāi)這個(gè)鏈接 )這是客戶(hù)端的index.htl:

還有JavaScript文件main.js:
完整的服務(wù)端:var static = require('node-static');
var http = require('http');
var file = new(static.Server)();
var app = http.createServer(function (req, res) {
file.serve(req, res);
}).listen(2013);

var io = require('socket.io').listen(app);

io.sockets.on('connection', function (socket){

// convenience function to log server messages to the client
function log(){
var array = ['>>> Message from server: '];
for (var i = 0; i < arguments.length; i++) {
array.push(arguments[i]);
}
socket.emit('log', array);
}

socket.on('message', function (message) {
log('Got message:', message);
// for a real app, would be room only (not broadcast)
socket.broadcast.emit('message', message);
});

socket.on('create or join', function (room) {
var numClients = io.sockets.clients(room).length;

log('Room ' + room + ' has ' + numClients + ' client(s)');
log('Request to create or join room ' + room);

if (numClients === 0){
  socket.join(room);
  socket.emit('created', room);
} else if (numClients === 1) {
  io.sockets.in(room).emit('join', room);
  socket.join(room);
  socket.emit('joined', room);
} else { // max two clients
  socket.emit('full', room);
}
socket.emit('emit(): client ' + socket.id + ' joined room ' + room);
socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room);

});

});

復(fù)制代碼
要運(yùn)行這個(gè)app,你需要安裝Node, socket.io and node-static叮贩』魇ǎ可以在 nodejs.org下載Node佛析,再安裝 socket.io 和node-static,在終端運(yùn)行Node Package Manager:npm install socket.ionpm install node-static啟動(dòng)服務(wù)彪蓬,運(yùn)行下面命令node server.js在瀏覽器打開(kāi) localhost:2013.用新的瀏覽器打開(kāi)localhost:2013 寸莫,用控制臺(tái)看下發(fā)生了什么使用 RTCDataChannel交換信息初始化一個(gè)WebRTC會(huì)話(huà),必須有一個(gè)信令 服務(wù)器档冬。然而膘茎,一旦兩端確定了 一個(gè)通話(huà),理論上酷誓,RTCDataChannel可以接替信令通道披坏,這可以減少信號(hào)的延遲。一旦信息直接在兩端通訊盐数,RTCDataChannel會(huì)幫忙減少帶寬使用和進(jìn)程開(kāi)銷(xiāo)棒拂。沒(méi)有例子,但可以看下面:信令性能和擴(kuò)展性1.RTCPeerConnection 不會(huì)搜集candidates娘扩,直到setLocalDescription() 被調(diào)用着茸。這個(gè)被JSEP IETF draft.強(qiáng)制要求了。2.利用Trickle ICE(看上面解釋?zhuān)航邮盏絚andidates后立即調(diào)用addIceCandidate()琐旁,現(xiàn)成的信令服務(wù) 這里有一些可以用的WebRTC signaling服務(wù)端:webRTC.io: 第一個(gè)抽象庫(kù) for WebRTC.
easyRTC: 一個(gè)完整的WebRTC包 a full-stack WebRTC package.
Signalmaster:一個(gè)使用 SimpleWebRTCJavaScrip客戶(hù)端庫(kù)的signaling服務(wù)

如果你一點(diǎn)都不想編碼涮阔,你可以用完整的商業(yè)WebRTC平臺(tái),像vLine, OpenTok and Asterisk愛(ài)立信創(chuàng)建了一個(gè) signaling server using PHP on Apache灰殴,在WebRTC早期的時(shí)候敬特,現(xiàn)在這個(gè)已經(jīng)被棄用了,但是如果你考慮到相似的情況牺陶,這個(gè)代碼還是值得一看的伟阔。六.Signaling安全 Security is the art of making nothing happen.
Salman Rushdie
所有WebRTC 組件都被強(qiáng)制加密。
但是信令機(jī)制沒(méi)有被WebRTC標(biāo)準(zhǔn)定義掰伸,所有確保信令安全取決于你皱炉,如果一個(gè)攻擊者想去劫持信令,他們會(huì)導(dǎo)致會(huì)話(huà)中止狮鸭,重定向鏈接和記錄合搅,改變或者注入內(nèi)容。

一個(gè)牢固的信令最重要的功能是使用加密協(xié)議歧蕉,HTTPS 和WSS (i.e TLS)可以確保信息不會(huì)非加密攔截灾部。
同時(shí),小心不要廣播信令信息惯退,不然攻擊者可以使用相同的信令服務(wù)鏈接其他來(lái)電用戶(hù)赌髓。
使用 TLS.去確保WebRTC應(yīng)用的安全。
信令交互完之后,使用ICE去處理NATs和防火墻對(duì)于元數(shù)據(jù)的信令锁蠕,WebRTC應(yīng)用可以使用中間服務(wù)夷野,但實(shí)際的媒體和數(shù)據(jù)流在一個(gè)會(huì)話(huà)確立后,RTCPeerConnection 嘗試去直連客戶(hù)端:P2P在一個(gè)簡(jiǎn)單的世界里匿沛,每一個(gè)WebRTC端都有一個(gè)唯一的地址扫责,這樣他可以與其他端交換數(shù)據(jù),以便直接 通訊逃呼。

實(shí)際情況下,大多數(shù)設(shè)備都在一個(gè)或多個(gè)NAT層后面者娱,有些有防毒軟件阻礙確定的端口和協(xié)議抡笼,還有很多在代理和公司的防火墻后面。防火墻和NAT實(shí)際上可能由一些類(lèi)似家庭wifi路由器產(chǎn)生的黄鳍。
WebRTC 可以使用ICE框架去克服真實(shí)世界的復(fù)雜網(wǎng)絡(luò)推姻。為了實(shí)現(xiàn)這個(gè)功能,你的應(yīng)用必須傳ICE服務(wù)地址給RTCPeerConnection框沟,如下所述藏古。ICE 試著尋找最佳路線(xiàn)去連接對(duì)方,它會(huì)并行的尋找所有可能性忍燥,然后選擇最有效的可行方式拧晕。 ICE首先會(huì)嘗試用設(shè)備系統(tǒng)或網(wǎng)卡獲取到的主機(jī)地址去建立連接;如果這個(gè)失敗了(設(shè)備在NATs后面就會(huì))ICE從STUN服務(wù)器獲得外部的地址梅垄,如果這個(gè)也失敗了厂捞,就用TURN中轉(zhuǎn)服務(wù)器做通訊。也就是說(shuō):
STUN服務(wù)器用來(lái)獲取外部網(wǎng)絡(luò)地址队丝。
如果P2P失敗的話(huà)靡馁,[size=14.4444446563721px]TURN服務(wù)器用來(lái)中繼通訊。
每一個(gè)TURN服務(wù)器都支持STUN:一個(gè)TURN服務(wù)器是由一個(gè)STUN服務(wù)器加上中繼功能机久。ICE也可以用來(lái)應(yīng)付復(fù)雜的NAT設(shè)置:
事實(shí)上臭墨,NAT的”打洞“可能需要除了公共IP之外的端口地址。
WebRTC應(yīng)用在iceServers配置對(duì)象(RTCPeerConnection constructor)里設(shè)置STUN and/or TURN服務(wù)器地址膘盖。
apprtc.appspot.com里這個(gè)值像這樣:
{
'iceServers': [
{
'url': 'stun:stun.l.google.com:19302'
},
{
'url': 'turn:192.158.29.39:3478?transport=udp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
},
{
'url': 'turn:192.158.29.39:3478?transport=tcp',
'credential': 'JZEOEt2V3Qb0y27GRntt2u2PAYA=',
'username': '28224511:1379330808'
}
]
}

復(fù)制代碼

一旦RTCPeerConnection 有了這些信息胧弛,ICE會(huì)自動(dòng)啟動(dòng):RTCPeerConnection 使用ICE框架計(jì)算出兩端間最佳路線(xiàn),需要STUN和TURN服務(wù)器衔憨。

STUNNATs會(huì)給它的設(shè)備提供一個(gè)內(nèi)部網(wǎng)絡(luò)IP地址叶圃,但這個(gè)地址不能在外網(wǎng)使用,因?yàn)闆](méi)有外網(wǎng)的地址践图,所有WebRTC沒(méi)辦法做連接掺冠,為解決這個(gè)問(wèn)題,WebRTC使用了STUN。STUN服務(wù)架設(shè)在外網(wǎng)德崭,它有一個(gè)簡(jiǎn)單的任務(wù):獲取一個(gè)發(fā)送請(qǐng)求的設(shè)備(運(yùn)行在NAT后邊的應(yīng)用)的IP和端口斥黑,然后返回這個(gè)地址。換句話(huà)說(shuō)眉厨,應(yīng)用使用STUN服務(wù)器發(fā)現(xiàn)它的外網(wǎng)IP和端口锌奴,這個(gè)過(guò)程確保了一個(gè)WebRTC端獲得它自己的公共地址,然后通過(guò)signaling機(jī)制發(fā)送這個(gè)信息給另一端憾股,這樣就可以建立起一個(gè)直接連接鹿蜀。(在實(shí)際中,不同的NATs有不同的工作方式服球,可能有多個(gè)NAT層茴恰,但是原理是一樣的)STUN服務(wù)器不需要做太多工作和存儲(chǔ)太多東西,所以簡(jiǎn)單的STUN服務(wù)器可以應(yīng)付大量的請(qǐng)求斩熊。根據(jù) webrtcstats.com的統(tǒng)計(jì)往枣,使用STUN方式建立WebRTC通話(huà)的成功率有86%的。

TURNRTCPeerConnection 會(huì)試著用UDP在兩端建立一個(gè)直連粉渠,如果失敗了分冈,RTCPeerConnection 會(huì)改用TCP,如果這個(gè)再失敗了霸株,TURN服務(wù)器會(huì)被作為后備方案使用雕沉,在兩端間中繼數(shù)據(jù)。重述:TURN是在兩端間中轉(zhuǎn)視頻/語(yǔ)音/數(shù)據(jù) 流淳衙,而不是發(fā)送數(shù)據(jù)蘑秽。
TURN 有個(gè)公共地址,所以每個(gè)端即使在防火墻或者代理后面箫攀,也能訪(fǎng)問(wèn)到肠牲。
TURN有個(gè)簡(jiǎn)單的任務(wù),中轉(zhuǎn)數(shù)據(jù)流靴跛,但不像STUN缀雳,TURN會(huì)花費(fèi)大量帶寬。所有梢睛,TURN需要夠強(qiáng)壯肥印。

圖表表示TURN的作用:?jiǎn)渭兊腟TUN不起作用,客戶(hù)端就會(huì)轉(zhuǎn)向使用TURN绝葡。
部署 STUN 和 TURN 服務(wù)器作為測(cè)試深碱,谷歌公布了一個(gè)公共的STUN服務(wù),stun.l.google.com:19302藏畅, apprtc.appspot.com用的就是這個(gè)敷硅。作為一個(gè)產(chǎn)品級(jí)別的 STUN/TURN服務(wù)器,我們建議使用 rfc5766-turn-server,STUN 和TURN的源碼可以從code.google.com/p/rfc5766-turn-server獲取绞蹦,這個(gè)鏈接也包括了部署的資料力奋。A VM image for Amazon Web Services is also available.本社區(qū)也發(fā)布了部署教程:部署教程一個(gè)可代替的TURN服務(wù)器是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 makesudo 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_PATHCopy restund.conf to /etc/restund.confSet restund.conf to use the right 10. IP address
Run restund
Test using stund client from remote machine: ./client IP:port

七.突破p2p:多人會(huì)議WebRTC你可以需要看下Justin Uberti提議的IETF標(biāo)識(shí):請(qǐng)求TURN服務(wù)的API很容易想象到一些場(chǎng)景不只是一對(duì)一的視頻通話(huà)景殷,舉個(gè)例子攀芯,公司小組需要一個(gè)視頻會(huì)議褒脯,或者一個(gè)公開(kāi)的演講衫生,一個(gè)演講者面對(duì)數(shù)百(或者數(shù)千)的觀看者屋谭。一個(gè)WebRTC應(yīng)用可以使用多個(gè)RTCPeerConnections,這樣每一個(gè)端可以連接其他端形成一個(gè)網(wǎng)絡(luò)并巍。talky.io就是使用這種方法實(shí)現(xiàn)咐扭,對(duì)于少數(shù)的用戶(hù)子寓,可以很好的工作梁厉。但是進(jìn)程和帶寬開(kāi)銷(xiāo)會(huì)非常大,特別是移動(dòng)客戶(hù)端踏兜。

在一個(gè)星型結(jié)構(gòu)里词顾,一個(gè)WebRTC客戶(hù)端可以選擇一個(gè)端去分布數(shù)據(jù)流給所有的用戶(hù),你可以自己設(shè)計(jì)重新分配機(jī)制的服務(wù)和構(gòu)造區(qū)實(shí)現(xiàn)這種方式(werrtc.org提供了一個(gè)樣例sample client application)從Chrome31和Opera18 開(kāi)始碱妆,從一個(gè)RTCPeerConnection 獲取的媒體流肉盹,可以作為對(duì)方的輸入:這里有個(gè)demosimpl.info/multi。這樣可以確保更靈活的結(jié)構(gòu)疹尾,因?yàn)樗梢栽试Sweb應(yīng)用通過(guò)選擇哪個(gè)用戶(hù)可以連接去控制一個(gè)通話(huà) 路由上忍。多點(diǎn)控制部件MCU(Multipoint Control Unit)大量用戶(hù)通話(huà)的更好解決方案是使用Multipoint Control Unit(MCU)。這是一個(gè)在大量參與者間分布媒體的橋接服務(wù)器纳本。MCUs可以在一個(gè)視頻 會(huì)議里處理不同的分辨率窍蓝,編解碼,和幀速率繁成。對(duì)于多端會(huì)議吓笙,有很多因素要考慮:最重要的是,從多個(gè)源里巾腕,怎么顯示多個(gè)視頻和混合音頻面睛。像 vLine 的云平臺(tái)也在致力于優(yōu)化傳輸路由。

你可以去買(mǎi)一個(gè)MCU硬件或者自己搭一個(gè)尊搬。

Cisco MCU背部有幾個(gè)開(kāi)源的MCU硬件款可以選叁鉴,例如 Licode(以前稱(chēng)為L(zhǎng)ynckia) 生產(chǎn)的開(kāi)源 MCU for WebRTC; OpenTok 平臺(tái)的Mantis.突破瀏覽器: VoIP, telephones 和 messagingWebRTC 的標(biāo)準(zhǔn)讓瀏覽器和不同設(shè)備不同平臺(tái),例如手機(jī)或者一個(gè)視頻會(huì)議系統(tǒng)佛寿,進(jìn)行通話(huà)稱(chēng)為可能幌墓。SIP是一種信令協(xié)議,用來(lái)做VoIP和視頻系統(tǒng)。為了讓W(xué)ebRTC和SIP端通訊克锣,WebRTC需要一個(gè)代理服務(wù)器去調(diào)解信令茵肃。信令一定會(huì)經(jīng)過(guò)網(wǎng)關(guān),但是一旦會(huì)話(huà)建立袭祟,視頻和語(yǔ)音就能在兩端傳輸验残。PSTN,公共電話(huà)交換網(wǎng)絡(luò)巾乳,是舊式模擬電話(huà)的交換網(wǎng)絡(luò)您没。為了WebRTC和電話(huà)進(jìn)行通話(huà),必須通過(guò)一個(gè)PSTN網(wǎng)關(guān)胆绊。同理氨鹏,要讓W(xué)ebRTC跟Jingle端(像IM客戶(hù)端)通訊,需要一個(gè)中間XMPP服務(wù)器压状。Jingle作為XMPP的擴(kuò)展仆抵,用來(lái)實(shí)現(xiàn)視頻和語(yǔ)音能夠作為信息服務(wù):現(xiàn)在的WebRTC就是基于C++實(shí)現(xiàn)libjingle 庫(kù)發(fā)展來(lái)的,Jingle最初是Google Talk的技術(shù)种冬。一堆應(yīng)用庫(kù)镣丑,平臺(tái)讓W(xué)ebRTC能在實(shí)際中通訊:sipML5:一個(gè) 開(kāi)源的 JavaScript SIP 客戶(hù)端
jsSIP: JavaScript SIP庫(kù)
Phono: 開(kāi)源JavaScript phone API, 作為一個(gè)插件
Zingaya: 一個(gè)嵌入式電話(huà)部件
Twilio: 音頻和信息
Uberconference: 會(huì)議技術(shù)

sipML5 的開(kāi)發(fā)者也開(kāi)發(fā)了webrtc2sip的網(wǎng)關(guān)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! 發(fā)現(xiàn)更多WebRTC codelab: 一步一步教你怎么打造一個(gè)視頻和文本聊天應(yīng)用,使用Socket.io Signaling服務(wù)娱两。在Node上面跑莺匠。2013 Google I/O WebRTC presentation WebRTC領(lǐng)頭人Justin Uberti.Chris Wilson 關(guān)于WebRTC的介紹:Introduction to WebRTC Apps.
WebRTC Book 這本書(shū)有很多關(guān)于數(shù)據(jù)、signaling十兢、網(wǎng)絡(luò)拓?fù)浣Y(jié)構(gòu)的細(xì)節(jié)趣竣。
WebRTC and Signaling: What Two Years Has Taught Us:介紹為什么把signaling脫離出標(biāo)準(zhǔn)是個(gè)好主意
A Practical Guide to Building WebRTC Apps:提供很多WebRTC的技術(shù)和基礎(chǔ)建設(shè)信息。
WebRTC chapter 深入研究WebRTC的結(jié)構(gòu)旱物,使用案例和性能遥缕。

最后編輯于
?著作權(quán)歸作者所有,轉(zhuǎn)載或內(nèi)容合作請(qǐng)聯(lián)系作者
  • 序言:七十年代末,一起剝皮案震驚了整個(gè)濱河市异袄,隨后出現(xiàn)的幾起案子通砍,更是在濱河造成了極大的恐慌,老刑警劉巖烤蜕,帶你破解...
    沈念sama閱讀 218,755評(píng)論 6 507
  • 序言:濱河連續(xù)發(fā)生了三起死亡事件封孙,死亡現(xiàn)場(chǎng)離奇詭異,居然都是意外死亡讽营,警方通過(guò)查閱死者的電腦和手機(jī)虎忌,發(fā)現(xiàn)死者居然都...
    沈念sama閱讀 93,305評(píng)論 3 395
  • 文/潘曉璐 我一進(jìn)店門(mén),熙熙樓的掌柜王于貴愁眉苦臉地迎上來(lái)橱鹏,“玉大人膜蠢,你說(shuō)我怎么就攤上這事堪藐。” “怎么了挑围?”我有些...
    開(kāi)封第一講書(shū)人閱讀 165,138評(píng)論 0 355
  • 文/不壞的土叔 我叫張陵礁竞,是天一觀的道長(zhǎng)。 經(jīng)常有香客問(wèn)我杉辙,道長(zhǎng)模捂,這世上最難降的妖魔是什么? 我笑而不...
    開(kāi)封第一講書(shū)人閱讀 58,791評(píng)論 1 295
  • 正文 為了忘掉前任蜘矢,我火速辦了婚禮狂男,結(jié)果婚禮上,老公的妹妹穿的比我還像新娘品腹。我一直安慰自己岖食,他們只是感情好,可當(dāng)我...
    茶點(diǎn)故事閱讀 67,794評(píng)論 6 392
  • 文/花漫 我一把揭開(kāi)白布舞吭。 她就那樣靜靜地躺著泡垃,像睡著了一般。 火紅的嫁衣襯著肌膚如雪羡鸥。 梳的紋絲不亂的頭發(fā)上兔毙,一...
    開(kāi)封第一講書(shū)人閱讀 51,631評(píng)論 1 305
  • 那天,我揣著相機(jī)與錄音兄春,去河邊找鬼。 笑死锡溯,一個(gè)胖子當(dāng)著我的面吹牛赶舆,可吹牛的內(nèi)容都是我干的。 我是一名探鬼主播祭饭,決...
    沈念sama閱讀 40,362評(píng)論 3 418
  • 文/蒼蘭香墨 我猛地睜開(kāi)眼芜茵,長(zhǎng)吁一口氣:“原來(lái)是場(chǎng)噩夢(mèng)啊……” “哼!你這毒婦竟也來(lái)了倡蝙?” 一聲冷哼從身側(cè)響起九串,我...
    開(kāi)封第一講書(shū)人閱讀 39,264評(píng)論 0 276
  • 序言:老撾萬(wàn)榮一對(duì)情侶失蹤,失蹤者是張志新(化名)和其女友劉穎寺鸥,沒(méi)想到半個(gè)月后猪钮,有當(dāng)?shù)厝嗽跇?shù)林里發(fā)現(xiàn)了一具尸體,經(jīng)...
    沈念sama閱讀 45,724評(píng)論 1 315
  • 正文 獨(dú)居荒郊野嶺守林人離奇死亡胆建,尸身上長(zhǎng)有42處帶血的膿包…… 初始之章·張勛 以下內(nèi)容為張勛視角 年9月15日...
    茶點(diǎn)故事閱讀 37,900評(píng)論 3 336
  • 正文 我和宋清朗相戀三年烤低,在試婚紗的時(shí)候發(fā)現(xiàn)自己被綠了。 大學(xué)時(shí)的朋友給我發(fā)了我未婚夫和他白月光在一起吃飯的照片笆载。...
    茶點(diǎn)故事閱讀 40,040評(píng)論 1 350
  • 序言:一個(gè)原本活蹦亂跳的男人離奇死亡扑馁,死狀恐怖涯呻,靈堂內(nèi)的尸體忽然破棺而出,到底是詐尸還是另有隱情腻要,我是刑警寧澤复罐,帶...
    沈念sama閱讀 35,742評(píng)論 5 346
  • 正文 年R本政府宣布,位于F島的核電站雄家,受9級(jí)特大地震影響效诅,放射性物質(zhì)發(fā)生泄漏。R本人自食惡果不足惜咳短,卻給世界環(huán)境...
    茶點(diǎn)故事閱讀 41,364評(píng)論 3 330
  • 文/蒙蒙 一填帽、第九天 我趴在偏房一處隱蔽的房頂上張望。 院中可真熱鬧咙好,春花似錦篡腌、人聲如沸。這莊子的主人今日做“春日...
    開(kāi)封第一講書(shū)人閱讀 31,944評(píng)論 0 22
  • 文/蒼蘭香墨 我抬頭看了看天上的太陽(yáng)。三九已至层宫,卻和暖如春杨伙,著一層夾襖步出監(jiān)牢的瞬間,已是汗流浹背萌腿。 一陣腳步聲響...
    開(kāi)封第一講書(shū)人閱讀 33,060評(píng)論 1 270
  • 我被黑心中介騙來(lái)泰國(guó)打工限匣, 沒(méi)想到剛下飛機(jī)就差點(diǎn)兒被人妖公主榨干…… 1. 我叫王不留,地道東北人毁菱。 一個(gè)月前我還...
    沈念sama閱讀 48,247評(píng)論 3 371
  • 正文 我出身青樓米死,卻偏偏與公主長(zhǎng)得像,于是被迫代替她去往敵國(guó)和親贮庞。 傳聞我的和親對(duì)象是個(gè)殘疾皇子峦筒,可洞房花燭夜當(dāng)晚...
    茶點(diǎn)故事閱讀 44,979評(píng)論 2 355

推薦閱讀更多精彩內(nèi)容