做者:李超,音視頻技術專家。本入門教程將分爲三篇內容,分別講述信令服務器的搭建、媒體服務器的搭建、Android 端的 WebRTC 應用實現,全文采用開源框架來搭建,適用於大多數入門的開發者。轉載請註明出處。 如遇到 WebRTC 開發問題,能夠點擊這裏,關注做者與他交流。
咱們在學習 WebRTC 時,首先要把實驗環境搭建好,這樣咱們就能夠在上面作各類實驗了。javascript
對於 WebRTC 來講,它有一整套規範,如怎樣使用它的接口、使用SDP進行媒體協商、經過ICE收集地址並進行連通性檢測等等。除此以外,WebRTC還須要房間服務器將多端彙集到一塊兒管理,以及信令服務器進行信令數據交換(如媒體描述信息SDP的交換,鏈接地址的交抽換等),但在WebRTC的規範中沒有對這部份內容進行規定,因此須要由用戶本身處理。html
你能夠根據本身的喜愛選擇服務器(如 Apache,Nginx 或 Nodejs),我今天將介紹如何使用 Nodejs 來搭建信令服務器。前端
Apache、Nginx和Nodejs都是很是成熟的Web服務器,Nginx 能夠說是的性能是最好的Web服務器了。但從將來的發展來講,Nodejs可能會更有優點。java
如今以Chrome爲表明的瀏覽器的功能愈來愈強大,之前認爲經過瀏覽器不可能完成的事兒,如今它均可以輕鬆實現。H5、 WebSocket的出現以及如今WebRTC的加入,讓你們愈來愈以爲之後的瀏覽器能夠說是「無所不能」。所以,推進 JavaScript 語言的發展愈來愈迅速。這能夠從如今 JavaScript 技術的火爆,以及各類層疊不窮JS FrameWork的出現得以印證。node
而 Nodejs 的最大優勢便是可使用 JS 語言開發服務器程序。這樣使得大量的前端同窗能夠無縫的轉到服務器開發,甚至有可能先後端使用同一套代碼實現。對於這一點我想不管是對我的仍是對於企業都是具大的誘惑。git
Nodejs 如今是很是流行的 Web 服務器,它在服務器端使用 V8(JavaScript)引擎,經過它解析 JS 腳原本控制服務器的行爲。這對於廣大的 JS 同窗來講真是太幸福了,在10年前還很難想像能夠經過 JS 腳本語言來寫服務器程序。github
固然,若是你想對Nodejs做能力拓展的話,仍是要寫C/C++庫,而後加載到 Nodejs 中去。npm
Nodejs的工做原理如上圖所示, 其核心是 V8 引擎。經過該引擎,可讓 js 調用 C/C++方法 或 對象。相反,經過它也可能讓 C/C++ 訪問 javascript 方法和變量。後端
Nodejs 首先將 JavaScript 寫好的應用程序交給 V8 引擎進行解析,V8理解應用程序的語義後,再調用 Nodejs 底層的 C/C++ API將服務啓動起來。 因此 Nodejs 的強大就在於 js 能夠直接調用 C/C++ 的方法,使其能力能夠無限擴展。瀏覽器
以開發一個 HTTP 服務爲例,Nodejs 打開偵聽的服務端口後,底層會調用 libuv 處理該端口的全部 http 請求。其網絡事件處理以下圖所示:
當有網絡請求過來時,首先會被插入到一個事件處理隊列中。libuv會監控該事件隊列,當發現有事件時,先對請求作判斷,若是是簡單的請求,就直接返回響應了;若是是複雜請求,則從線程池中取一個線程進行異步處理;
線程處理完後,有兩種可能:一種是已經處理完成,則向用戶發送響應;另外一種狀況是還須要進一步處理,則再生成一個事件插入到事件隊列中等待處理;事件處理就這樣循環往復下去,永不停歇。
如上圖所示,在咱們使用 Nodejs以後實際存在了兩個 V8 引擎。一個V8用於解析服務端的 JS 應用程序,它將服務啓動起來。另外一個 V8 是瀏覽器中的 V8 引擎,用於控制瀏覽器的行爲。
對於使用 Nodejs 的新手來講,很容易出現思惟混亂,由於在服務端至少要放兩個 JS 腳本。其中一個是服務端程序,控制 Nodejs 的行爲,它由 Nodejs 的V8引擎解析處理;另外一個是客戶端程序,它是要由瀏覽器請求後,下發到瀏覽器,由瀏覽器中的 V8 引擎進行解析處理。若是分不清這個,那麻煩就大了。
下面咱們就來看看具體如何安裝 Nodejs。
安裝 Nodejs 很是的簡單:
在Ubuntu系統下執行:
apt install nodejs
或在Mac 系統下執行:
brew install nodejs
經過上面的步驟咱們就將 Nodejs 安裝好了。我這裏安裝的 Nodejs版本爲:v8.10.0。
除了安裝 Nodejs 以外,咱們還要安裝NPM(Node Package Manager),也就是 Nodejs 的包管理器。它就像Ubuntu下的 apt 或Mac 系統下的brew 命令相似,是專門用來管理各類依賴庫的。
在它們沒有出現以前,咱們要安裝個包特別麻煩。以Linux爲例,假設要安裝一個工具,其基本步驟是:
你們能夠看到,之前在Linux下安裝個程序或工具是多麼的麻煩。
Linux 有了apt 以後,一切都變得簡單了。咱們只要執行 apt install xxx 一條命令就行了,它會幫你完成上面的一堆操做。
對於 Nodejs的安裝包也是如此,NPM 就是至關於 Linux 下的 apt,它的出現大大提升了人們的工做效率。
NPM 的安裝像安裝 Nodejs 同樣簡單:
在Ubuntu下執行:
apt install npm
或在Mac下執行:
brew install npm
這次,咱們使用 Nodejs 下的 socket.io 庫來實現 WebRTC 信令服務器。socket.io特別適合用來開發WebRTC的信令服務器,經過它來構建信令服務器特別的簡單,這主要是由於它內置了房間 的概念。
上圖是 socket.io 與 Nodejs配合使用的邏輯關係圖, 其邏輯很是簡單。socket.io 分爲服務端和客戶端兩部分。服務端由 Nodejs加載後偵聽某個服務端口,客戶端要想與服務端相連,首先要加載 socket.io 的客戶端庫,而後調用 io.connect();
就與服務端連上了。
須要特別強調的是 socket.io 消息的發送與接收。socket.io 有不少種發送消息的方式,其中最多見的有下面幾種,是咱們必需要撐握的:
給本次鏈接發消息
socket.emit()
給某個房間內全部人發消息
io.in(room).emit()
除本鏈接外,給某個房間內全部人發消息
socket.to(room).emit()
除本鏈接外,給因此人發消息
socket.broadcast.emit()
消息又該如何接收呢?
發送 command 命令
S: socket.emit('cmd’); C: socket.on('cmd',function(){...});
送了一個 command 命令,帶 data 數據
S: socket.emit('action', data); C: socket.on('action',function(data){...});
發送了command命令,還有兩個數據
S: socket.emit(action,arg1,arg2); C: socket.on('action',function(arg1,arg2){...});
有了以上這些知識,咱們就能夠實現信令數據通信了。
接下來咱們來看一下,如何經過 Nodejs下的 socket.io 來構建的一個服務器:
這是客戶端代碼,也就是在瀏覽器裏執行的代碼。index.html:
<!DOCTYPE html> <html> <head> <title>WebRTC client</title> </head> <body> <script src='/socket.io/socket.io.js'></script> <script src='js/client.js'></script> </body> </html>
該代碼十分簡單,就是在body裏引入了兩段 JS 代碼。其中,socket.io.js 是用來與服務端創建 socket 鏈接的。client.js 的做用是作一些業務邏輯,並最終經過 socket 與服務端通信。
首先,在server.js
目錄下建立 js
子目錄,而後在 js目錄下生成 client.js。
下面是client.js的代碼:
var isInitiator; room = prompt('Enter room name:'); //彈出一個輸入窗口 const socket = io.connect(); //與服務端創建socket鏈接 if (room !== '') { //若是房間不空,則發送 "create or join" 消息 console.log('Joining room ' + room); socket.emit('create or join', room); } socket.on('full', (room) => { //若是從服務端收到 "full" 消息 console.log('Room ' + room + ' is full'); }); socket.on('empty', (room) => { //若是從服務端收到 "empty" 消息 isInitiator = true; console.log('Room ' + room + ' is empty'); }); socket.on('join', (room) => { //若是從服務端收到 「join" 消息 console.log('Making request to join room ' + room); console.log('You are the initiator!'); }); socket.on('log', (array) => { console.log.apply(console, array); });
在該代碼中:
根據socket返回的消息作不一樣的處理:
以上是客戶端(也就是在瀏覽器)中執行的代碼。下面咱們來看一下服務端的處理邏輯:
服務器端代碼,server.js:
const static = require('node-static'); const http = require('http'); const file = new(static.Server)(); const app = http.createServer(function (req, res) { file.serve(req, res); }).listen(2013); const io = require('socket.io').listen(app); //偵聽 2013 io.sockets.on('connection', (socket) => { // convenience function to log server messages to the client function log(){ const array = ['>>> Message from server: ']; for (var i = 0; i < arguments.length; i++) { array.push(arguments[i]); } socket.emit('log', array); } socket.on('message', (message) => { //收到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', (room) => { //收到 「create or join」 消息 var clientsInRoom = io.sockets.adapter.rooms[room]; var numClients = clientsInRoom ? Object.keys(clientsInRoom.sockets).length : 0; //房間裏的人數 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); //發送 "created" 消息 } else if (numClients === 1) { //若是房間裏有一我的 io.sockets.in(room).emit('join', room); socket.join(room); socket.emit('joined', room); //發送 「joined」消息 } else { // max two clients socket.emit('full', room); //發送 "full" 消息 } socket.emit('emit(): client ' + socket.id + ' joined room ' + room); socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room); }); });
在服務端引入了 node-static 庫,使服務器具備發佈靜態文件的功能。服務器具備此功能後,當客戶端(瀏覽器)向服務端發起請求時,服務器經過該模塊得到客戶端(瀏覽器)運行的代碼,也就是上我面咱們講到的 index.html 和 client.js 並下發給客戶端(瀏覽器)。
服務端偵聽 2013 這個端口,對不一樣的消息作相應的處理:
要運行該程序,須要使用 NPM 安裝 socket.io 和 node-static,安裝方法以下:
進入到 server.js
所在的目錄,而後執行下面的命令。
npm install socket.io npm install node-static
經過上面的步驟咱們就使用 socket.io 構建好一個服務器,如今能夠經過下面的命令將服務啓動起來了:
node server.js
若是你是在本機上搭建的服務,則能夠在瀏覽器中輸入 localhost:2013 ,而後新建一個tab 在裏邊再次輸入localhost:2013 。此時,打開控制檯看看發生了什麼?
在Chrome下你可使用快捷鍵 Command-Option-J或Ctrl-Shift-J的DevTools訪問控制檯。
以上我向你們介紹了 Nodejs 的工做原理、Nodejs的安裝與佈署,以及如何使用 要sokcet.io 構建 WebRTC 信令消息服務器。socket.io 因爲有房間的概念因此與WebRTC很是匹配,用它開發WebRTC信令服務器很是方便。
另外,在本文中的例子只是一個簡單例子並無太多的實際價值。在後面的文章中我會以這個例子爲基礎,在其上面不斷增長一些功能,最終你會看到一個完整的Demo程序。