WebRTC背景
WebRTC弊攘,名稱源自網(wǎng)頁即時通信(英語:Web Real-Time Communication)的縮寫匹层,是一個支持網(wǎng)頁瀏覽器進行實時語音對話或視頻對話的API芒澜。它于2011年6月1日開源并在Google宏悦、Mozilla募胃、Opera支持下被納入萬維網(wǎng)聯(lián)盟的W3C推薦標準溉贿。
客戶端
創(chuàng)建RTCPeerConnection實例->發(fā)送candidate->創(chuàng)建offer->交換offer->添加媒體流
客戶端主要工作簡單來看就是三個交換,交換ice豆拨、交換offer直奋、交換流。通過代碼來看:
創(chuàng)建WebRTC實例
去除瀏覽器前綴轉(zhuǎn)換通用變量
const RTCPeerConnection = window.mozRTCPeerConnection || window.webkitRTCPeerConnection; const IceCandidate = window.mozRTCIceCandidate || window.RTCIceCandidate; const SessionDescription = window.mozRTCSessionDescription || window.RTCSessionDescription; navigator.getUserMedia = navigator.getUserMedia || navigator.mozGetUserMedia || navigator.webkitGetUserMedia;
創(chuàng)建WebRTC peer實例,它接收一個配置選項
configuration
施禾,其中配置了用來創(chuàng)建連接的 ICE 服務器信息脚线。const configuration = { // 內(nèi)網(wǎng)穿透使用sturn和turn服務信息,下面的服務器國內(nèi)無法使用弥搞,需要自行搭建殉挽,??見后文 iceServers: [ {urls: "stun:23.21.150.121"}, {urls: "stun:stun.l.google.com:19302"}, {urls: "turn:numb.viagenie.ca", credential: "webrtcdemo", username: "louis%40mozilla.com"} ] } const pc = new RTCPeerConnection(configuration); // 當 ICE 框架找到可以使用 Peer 創(chuàng)建連接的 “候選者”,會立即觸發(fā)一個事件(onicecandidate)拓巧。ICE候選者信息通過信令服務器轉(zhuǎn)發(fā) pc.onicecandidate = function (e) { if (!e.candidate) return; // 通過信令服務器轉(zhuǎn)發(fā)candidate 接收方通過addIceCandidate方法添加 send("icecandidate", JSON.stringify(e.candidate)); }; // ... function addIce(candidate){ // 信令服務器響應后 將遠端的candidate加入本地ice pc.addIceCandidate(new RTCIceCandidate(candidate)); }
ICECandidate
ICE全稱Interactive Connectivity Establishment:交互式連通建立方式斯碌。
ICE參照RFC5245建議實現(xiàn),是一組基于offer/answer模式解決NAT穿越的協(xié)議集合肛度。
它綜合利用現(xiàn)有的STUN傻唾,TURN等協(xié)議,以更有效的方式來建立會話承耿。客戶端側(cè)無需關心所處網(wǎng)絡的位置以及NAT類型冠骄,并且能夠動態(tài)的發(fā)現(xiàn)最優(yōu)的傳輸路徑。
簡單來說 發(fā)起方創(chuàng)建RTCPeerConnection實例后要將自己的candidate發(fā)送給接收方加袋,雙方互換candidate才可建立鏈接凛辣。
// 添加ice pc.addIceCandidate(new RTCIceCandidate(candidate))
交換offer SDP
Offer SDP 是用來向另一端描述期望格式(視頻, 格式, 解編碼, 加密, 解析度, 尺寸 等等)的元數(shù)據(jù)。
一次信息的交換需要從一端拿到 offer职烧,然后另外一端接受這個 offer 然后返回一個 answer扁誓。
// 創(chuàng)建offer await pc.createOffer({ // 是否接收音頻 offerToReceiveAudio: true, // 是否接收視頻 offerToReceiveVideo: true }); // 添加至本地描述 await pc.setLocalDescription(offer); // ... 將本地的offer通過信令服務器轉(zhuǎn)發(fā)給接收方 // 接收方收到遠端的offer 創(chuàng)建自己answer 并發(fā)送給主叫 await recvPc.setRemoteDescription(offer); await recvPc.setLocalDescription(await recvPc.createAnswer()); // ...將answer發(fā)送給主叫 // ...接收到被叫方返回的answer信息添加到setRemoteDescription pc.setRemoteDescription(answer);
添加流發(fā)送給對方
在接收到offer時我們就可以獲取本地的媒體流防泵,并添加到RTCPeerConnection實例中
//獲取媒體流 如果只想使用默認配置video直接賦值為true window.navigator.mediaDevices.getUserMedia({video: { // 幀數(shù) frameRate: { ideal: 10, max: 15 }, // 寬高 最大1280 最小800 ideal(應用最理想的)值時,這個值有著更高的權重蝗敢,意味著瀏覽器會先嘗試找到最接近指定的理想值的設定或者攝像頭(如果設備擁有不止一個攝像頭)捷泞。此外還要facingMode 前置或者后置攝像頭 width: { max: 1280,ideal: 1024,min:800 }, height: { max: 720,ideal: 960,min:600 } },audio:true}) .then(mediaStream => { console.log('addStream'); // 添加流到RTCPeerConnection實例中 this.pc.addStream(mediaStream); // 把本地的流添加到video標簽中播放 $('.my-video').srcObject = mediaStream; }); // 監(jiān)聽onaddstream pc.onaddstream=function(e){ // e.stream $('.your-best-friend-video').srcObject = e.stream; }
信令服務器
為客戶端交換offer交換ice的中轉(zhuǎn)站
使用nodejs創(chuàng)建一個簡單的WebSocket服務,如果想要在公網(wǎng)上必須要用wss,因為客戶端用的是https協(xié)議無法與ws協(xié)議連接寿谴,下面是如何創(chuàng)建wss:
const WebSocket = require('ws'); const https = require('https'); const options={ key:fs.readFileSync('./ssl/privkey.key','utf8'), cert:fs.readFileSync('./ssl/cert.crt','utf8') } // 添加證書及私鑰 證書可以使用openSSL生成锁右,或者申請免費的 const options={ key:fs.readFileSync('./ssl/privkey.key','utf8'), cert:fs.readFileSync('./ssl/cert.crt','utf8') } // 創(chuàng)建https服務 const httpsServer=https.createServer(options, function (req, res) { //要是單純的https連接的話就會返回這個東西 res.writeHead(403);//403即可 res.end("This is a WebSockets server!\n"); }).listen(15065) // 創(chuàng)建wss服務 const wsServer = new WebSocket.Server({ server: httpsServer }); wsServer.on('connection', (client, request) => { // 發(fā)送信息 client.send('xxx') // 接收消息 client.on('message', msg => {}) // 關閉連接 client.on('close', msg => {}) })
STURN/TURN中繼服務器
客戶端在局域網(wǎng)環(huán)境下想要與外網(wǎng)的機器通訊,需要NAT網(wǎng)絡穿越讶泰。
coturn是谷歌開源的中繼服務器應用咏瑟,集合了sturn 和 turn。
NAT兩種類型痪署,對稱NAT與非對稱NAT:
非對稱NAT码泞,每次請求對應的IP端口是不對應的(海王) 使用sturn就可以完成打洞穿越。 對稱NAT惠桃,每次請求對應的IP端口是對應的(5201314)浦夷,打洞穿越難度大 需要使用turn服務器中繼辖试。 通過一個場景解釋:客戶端向sturn服務器發(fā)請求辜王,獲得自己的對外ip和端口,此時客戶端是對稱NAT罐孝,這個對外暴露的端口只能給服務器使用呐馆,被叫客戶端無法使用主叫暴露的端口導致無法連接。如果是非對稱的莲兢,sturn服務器獲得主叫對外暴露的IP和端口汹来,告訴其他客戶端,其他客戶端就可以拿著這個ip和端口進行連接改艇。當然非對稱也有限制收班,具體分為三種,閱讀這篇文章[對稱與非對稱NAT](https://blog.csdn.net/yifuteli_kevin/article/details/8911261?utm_medium=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control&dist_request_id=&depth_1-utm_source=distribute.pc_relevant.none-task-blog-BlogCommendFromMachineLearnPai2-2.control)谒兄。
sturn和turn區(qū)別:
sturn幫助客戶端尋址摔桦,客戶端請求sturn服務器,返回給客戶端對外暴露的ip和端口承疲,使客戶端可以相互尋址邻耕,并p2p連接 turn 采用中繼的方式,占用資源大
建議在客戶端strun和turn都要配置燕鸽。
下載 安裝coturn
下載libevent-2.0 里面有coturn依賴的庫
wget https://github.com/downloads/libevent/libevent/libevent-2.0.21-stable.tar.gz tar zxvf libevent-2.0.21-stable.tar.gz cd libevent-2.0.21-stable && ./configure make && make install
下載編譯coturn
git clone https://github.com/coturn/coturn cd coturn ./configure make make install
配置
coturn有多種配置方式:命令行兄世、conf文件,數(shù)據(jù)庫啊研。我們使用配置文件的方式御滩。進入/usr/local/etc/目錄下有turnserver.conf.default鸥拧,復制為turnserver.conf
此外還需要OpenSSL
yum install openssl
使用OpenSSL生成cert和key
openssl req -x509 -newkey rsa:2048 -keyout /etc/turn_server_pkey.pem -out /etc/turn_server_cert.pem -days 99999 -nodes
獲取網(wǎng)卡信息ifconfig,記下內(nèi)網(wǎng)地址
進入配置文件并修改
relay-device=eth0 #與前ifconfig查到的網(wǎng)卡名稱一致 listening-ip=172.18.77.60 #內(nèi)網(wǎng)IP listening-port=3478 external-ip=47.107.110.xxx #公網(wǎng)IP relay-threads=50 cert=/etc/turn_server_cert.pem pkey=/etc/turn_server_pkey.pem min-port=49152 max-port=65535 user=user:123456 #用戶名密碼艾恼,創(chuàng)建IceServer時用 realm=xxxxxx
啟動turnserver
turnserver -o -a -f -user=user:123456 -r cwtlab
測試
可以使用自帶的工具進行測試也可以通谷歌提供的測試sturn/turn網(wǎng)站
使用自動工具
# 測試 STUN turnutils_stunclient 47.107.110.xxx # 測試 TURN turnutils_uclient -u user -w 123456 47.107.110.xxx
進入源碼所根目錄查看相關文檔
README.turnserver 查看turnserver相關指令及介紹
README.turnadmin 查看turnadmin 相關指令及介紹
README.turnutils 查看turnutils 測試
相關程序位置bin目錄下