數(shù)軸是一條直線對嗎 有人說一條直線是一條數(shù)軸對不對為什么
2024-10-07
更新時(shí)間:2024-09-02 18:04:13作者:未知
前言
我們在學(xué)習(xí) WebRTC 時(shí),首先要把實(shí)驗(yàn)環(huán)境搭建好,這樣我們就可以在上面做各種實(shí)驗(yàn)了。
對于 WebRTC 來說,它有一整套規(guī)范,如怎樣使用它的接口、使用SDP進(jìn)行媒體協(xié)商、通過ICE收集地址并進(jìn)行連通性檢測等等。除此之外,WebRTC還需要房間服務(wù)器將多端聚集到一起管理,以及信令服務(wù)器進(jìn)行信令數(shù)據(jù)交換(如媒體描述信息SDP的交換,連接地址的交抽換等),但在WebRTC的規(guī)范中沒有對這部分內(nèi)容進(jìn)行規(guī)定,所以需要由用戶自己處理。
你可以根據(jù)自己的喜好選擇服務(wù)器(如 Apache,Nginx 或 Nodejs),我今天將介紹如何使用 Nodejs 來搭建信令服務(wù)器。
為什么選擇 Nodejs
Apache、Nginx和Nodejs都是非常成熟的Web服務(wù)器,Nginx 可以說是的性能是最好的Web服務(wù)器了。但從未來的發(fā)展來說,Nodejs可能會更有優(yōu)勢。
現(xiàn)在以Chrome為代表的瀏覽器的功能越來越強(qiáng)大,以前認(rèn)為通過瀏覽器不可能完成的事兒,現(xiàn)在它都可以輕松實(shí)現(xiàn)。H5? WebSocket的出現(xiàn)以及現(xiàn)在WebRTC的加入,讓大家越來越覺得以后的瀏覽器可以說是“無所不能”。因此,推動(dòng) JavaScript 語言的發(fā)展越來越迅速。這可以從現(xiàn)在 JavaScript 技術(shù)的火爆,以及各種層出不窮JS FrameWork的出現(xiàn)得以印證。
而 Nodejs 的最大的優(yōu)點(diǎn)即是可以使用 JS 語言開發(fā)服務(wù)器程序。這樣使得大量的前端同學(xué)可以無縫地轉(zhuǎn)到服務(wù)器開發(fā),甚至有可能前后端使用同一套代碼實(shí)現(xiàn)。對于這一點(diǎn)我想無論是對個(gè)人還是對于企業(yè)都是巨大的誘惑。
一方面 JS 語言的簡單性可以方便的開發(fā)出各種各樣功能的服務(wù)端程序。
更可貴的是 Nodejs 的生態(tài)鏈非常的完整,有各種各樣的功能庫。你可以根據(jù)自己的需要通過安裝工具 NPM 快速的安裝,這也使它也得到了廣大開發(fā)者的喜歡。
Nodejs 現(xiàn)在是非常流行的 Web 服務(wù)器,它在服務(wù)器端使用 V8(JavaScript)引擎,通過它解析 JS 腳本來控制服務(wù)器的行為。這對于廣大的 JS 同學(xué)來說真是太幸福了,在10年前還很難想像可以通過 JS 腳本語言來寫服務(wù)器程序。
當(dāng)然,如果你想對Nodejs作能力拓展的話,還是要寫C/C++庫,然后加載到 Nodejs 中去。
Nodejs的基本原理
Nodejs的工作原理如上圖所示, 其核心是 V8 引擎。通過該引擎,可以讓 js 調(diào)用 C/C++方法 或 對象。相反,通過它也可能讓 C/C++ 訪問 javascript 方法和變量。
Nodejs 首先將 JavaScript 寫好的應(yīng)用程序交給 V8 引擎進(jìn)行解析,V8理解應(yīng)用程序的語義后,再調(diào)用 Nodejs 底層的 C/C++ API將服務(wù)啟動(dòng)起來。 所以 Nodejs 的強(qiáng)大就在于 js 可以直接調(diào)用 C/C++ 的方法,使其能力可以無限擴(kuò)展。
以開發(fā)一個(gè) HTTP 服務(wù)為例,Nodejs 打開偵聽的服務(wù)端口后,底層會調(diào)用 libuv 處理該端口的所有 http 請求。其網(wǎng)絡(luò)事件處理如下圖所示:
當(dāng)有網(wǎng)絡(luò)請求過來時(shí),首先會被插入到一個(gè)事件處理隊(duì)列中。libuv會監(jiān)控該事件隊(duì)列,當(dāng)發(fā)現(xiàn)有事件時(shí),先對請求做判斷,如果是簡單的請求,就直接返回響應(yīng)了;如果是復(fù)雜請求,則從線程池中取一個(gè)線程進(jìn)行異步處理;
線程處理完后,有兩種可能:一種是已經(jīng)處理完成,則向用戶發(fā)送響應(yīng);另一種情況是還需要進(jìn)一步處理,則再生成一個(gè)事件插入到事件隊(duì)列中等待處理;事件處理就這樣循環(huán)往復(fù)下去,永不停歇。
兩個(gè) V8 引擎
如上圖所示,在我們使用 Nodejs之后實(shí)際存在了兩個(gè) V8 引擎。一個(gè)V8用于解析服務(wù)端的 JS 應(yīng)用程序,它將服務(wù)啟動(dòng)起來。另一個(gè) V8 是瀏覽器中的 V8 引擎,用于控制瀏覽器的行為。
對于使用 Nodejs 的新手來說,很容易出現(xiàn)思維混亂,因?yàn)樵诜?wù)端至少要放兩個(gè) JS 腳本。其中一個(gè)是服務(wù)端程序,控制 Nodejs 的行為,它由 Nodejs 的V8引擎解析處理;另一個(gè)是客戶端程序,它是要由瀏覽器請求后,下發(fā)到瀏覽器,由瀏覽器中的 V8 引擎進(jìn)行解析處理。如果分不清這個(gè),那麻煩就大了。
安裝 Nodejs
下面我們就來看看具體如何安裝 Nodejs。
安裝 Nodejs 非常的簡單:
在Ubuntu系統(tǒng)下執(zhí)行:
apt install nodejs
或在Mac 系統(tǒng)下執(zhí)行:
brew install nodejs
通過上面的步驟我們就將 Nodejs 安裝好了。我這里安裝的 Nodejs版本為:v8.10.0。
安裝NPM
除了安裝 Nodejs 之外,我們還要安裝NPM(Node Package Manager),也就是 Nodejs 的包管理器。它就像Ubuntu下的 apt 或Mac 系統(tǒng)下的brew 命令類似,是專門用來管理各種依賴庫的。
在它們沒有出現(xiàn)之前,我們要安裝個(gè)包特別麻煩。以Linux為例,假設(shè)要安裝一個(gè)工具,其基本步驟是:
先將這個(gè)工具的源碼下載下來。
執(zhí)行./configure 生成Makefile 文件。
執(zhí)行 make 命令對其進(jìn)行編譯。
最后,執(zhí)行 make install 將其安裝到指定目錄下。
如果編譯過程中發(fā)現(xiàn)有依賴的庫,則要對依賴庫執(zhí)行前面的4步,也就是先將依賴庫安裝好,然后再來安裝該工具。
大家可以看到,以前在Linux下安裝個(gè)程序或工具是多么的麻煩。
Linux 有了apt 之后,一切都變得簡單了。我們只要執(zhí)行 apt install xxx 一條命令就好了,它會幫你完成上面的一堆操作。
對于 Nodejs的安裝包也是如此,NPM 就是相當(dāng)于 Linux 下的 apt,它的出現(xiàn)大大提高了人們的工作效率。
NPM 的安裝像安裝 Nodejs 一樣簡單:
在Ubuntu下執(zhí)行:
apt install npm
或在Mac下執(zhí)行:
brew install npm
socket.io
此次,我們使用 Nodejs 下的 socket.io 庫來實(shí)現(xiàn) WebRTC 信令服務(wù)器。socket.io特別適合用來開發(fā)WebRTC的信令服務(wù)器,通過它來構(gòu)建信令服務(wù)器特別的簡單,這主要是因?yàn)樗鼉?nèi)置了**房間** 的概念。
上圖是 socket.io 與 Nodejs配合使用的邏輯關(guān)系圖, 其邏輯非常簡單。socket.io 分為服務(wù)端和客戶端兩部分。服務(wù)端由 Nodejs加載后偵聽某個(gè)服務(wù)端口,客戶端要想與服務(wù)端相連,首先要加載 socket.io 的客戶端庫,然后調(diào)用 `io.connect();`就與服務(wù)端連上了。
需要特別強(qiáng)調(diào)的是 socket.io 消息的發(fā)送與接收。socket.io 有很多種發(fā)送消息的方式,其中最常見的有下面幾種,是我們必須要撐握的:
給本次連接發(fā)消息
socket.emit()
給某個(gè)房間內(nèi)所有人發(fā)消息
io.in(room).emit()
除本連接外,給某個(gè)房間內(nèi)所有人發(fā)消息
socket.to(room).emit()
除本連接外,給所以人發(fā)消息
socket.broadcast.emit()
消息又該如何接收呢?
發(fā)送 command 命令
S: socket.emit('cmd’);C: socket.on('cmd',function(){...});
送了一個(gè) command 命令,帶 data 數(shù)據(jù)
S: socket.emit('action', data);C: socket.on('action',function(data){...});
發(fā)送了command命令,還有兩個(gè)數(shù)據(jù)
S: socket.emit(action,arg1,arg2);C: socket.on('action',function(arg1,arg2){...});
有了以上這些知識,我們就可以實(shí)現(xiàn)信令數(shù)據(jù)通訊了。
搭建信令服務(wù)器
接下來我們來看一下,如何通過 Nodejs下的 socket.io 來構(gòu)建的一個(gè)服務(wù)器:
這是客戶端代碼,也就是在瀏覽器里執(zhí)行的代碼。index.html:
<!DOCTYPE html><html> <head> <title>WebRTC client</title> </head> <body> <script src='/socket.io/socket.io.js?x96866'></script> <script src='js/client.js'></script> </body></html>
該代碼十分簡單,就是在body里引入了兩段 JS 代碼。其中,socket.io.js 是用來與服務(wù)端建立 socket 連接的。client.js 的作用是做一些業(yè)務(wù)邏輯,并最終通過 socket 與服務(wù)端通訊。
首先,在 server.js 目錄下創(chuàng)建 js 的目錄,然后在 js目錄下生成 client.js。
下面是client.js的代碼:
var isInitiator;room = prompt('Enter room name:'); //彈出一個(gè)輸入窗口const socket = io.connect(); //與服務(wù)端建立socket連接if (room !== '') { //如果房間不空,則發(fā)送 "create or join" 消息 console.log('Joining room ' + room); socket.emit('create or join', room);}socket.on('full', (room) => { //如果從服務(wù)端收到 "full" 消息 console.log('Room ' + room + ' is full');});socket.on('empty', (room) => { //如果從服務(wù)端收到 "empty" 消息 isInitiator = true; console.log('Room ' + room + ' is empty');});socket.on('join', (room) => { //如果從服務(wù)端收到 “join" 消息 console.log('Making request to join room ' + room); console.log('You are the initiator!');});socket.on('log', (array) => { console.log.apply(console, array);});
在該代碼中:
首先彈出一個(gè)輸入框,要求用戶寫入要加入的房間。然后,通過 io.connect() 建立與服務(wù)端的連接,根據(jù)socket返回的消息做不同的處理:
當(dāng)收到房間滿”full”時(shí)的情況;
當(dāng)收到房間空“empty”時(shí)的情況;
當(dāng)收到加入“join”時(shí)的情況;
以上是客戶端(也就是在瀏覽器)中執(zhí)行的代碼。下面我們來看一下服務(wù)端的處理邏輯:
服務(wù)器端代碼,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); //偵聽 2013io.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時(shí),進(jìn)行廣播 log('Got message:', message); // for a real app, would be room only (not broadcast) socket.broadcast.emit('message', message); //在真實(shí)的應(yīng)用中,應(yīng)該只在房間內(nèi)廣播 }); 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; //房間里的人數(shù) 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); //發(fā)送 "created" 消息 } else if (numClients === 1) { //如果房間里有一個(gè)人 io.sockets.in(room).emit('join', room); socket.join(room); socket.emit('joined', room); //發(fā)送 “joined”消息 } else { // max two clients socket.emit('full', room); //發(fā)送 "full" 消息 } socket.emit('emit(): client ' + socket.id + ' joined room ' + room); socket.broadcast.emit('broadcast(): client ' + socket.id + ' joined room ' + room); });});
在服務(wù)端引入了 node-static 庫,使服務(wù)器具有發(fā)布靜態(tài)文件的功能。服務(wù)器具有此功能后,當(dāng)客戶端(瀏覽器)向服務(wù)端發(fā)起請求時(shí),服務(wù)器通過該模塊獲得客戶端(瀏覽器)運(yùn)行的代碼,也就是上我面我們講到的 index.html 和 client.js 并下發(fā)給客戶端(瀏覽器)。
服務(wù)端偵聽 2013 這個(gè)端口,對不同的消息做相應(yīng)的處理:
– 服務(wù)器收到 message 消息時(shí),它會直接進(jìn)行廣播,所有連接到該服務(wù)器的客戶端都會收收廣播的消息。
– 服務(wù)端收到 “create or join”消息時(shí),它會對房間里有人數(shù)進(jìn)行統(tǒng)計(jì),如果房間里沒有人,則發(fā)送”created” 消息;如果房間里有一個(gè)人,發(fā)送”join”消息和“joined”消息;如果超過兩個(gè)人,發(fā)送”full”消息。
要運(yùn)行該程序,需要使用 NPM 安裝 socket.io 和 node-static,安裝方法如下:
進(jìn)入到 server.js 所在的目錄,然后執(zhí)行下面的命令。
npm install socket.ionpm install node-static
啟動(dòng)服務(wù)器并測試
通過上面的步驟我們就使用 socket.io 構(gòu)建好一個(gè)服務(wù)器,現(xiàn)在可以通過下面的命令將服務(wù)啟動(dòng)起來了:
node server.js
如果你是在本機(jī)上搭建的服務(wù),則可以在瀏覽器中輸入 localhost:2013 ,然后新建一個(gè)tab 在里邊再次輸入localhost:2013 。此時(shí),打開控制臺看看發(fā)生了什么?
在Chrome下你可以使用快捷鍵 Command-Option-J或Ctrl-Shift-J的DevTools訪問控制臺。
小結(jié)
以上我向大家介紹了 Nodejs 的工作原理、Nodejs的安裝與部署,以及如何使用 要sokcet.io 構(gòu)建 WebRTC 信令消息服務(wù)器。socket.io 由于有房間的概念所以與WebRTC非常匹配,用它開發(fā)WebRTC信令服務(wù)器非常方便。
另外,在本文中的例子只是一個(gè)簡單例子并沒有太多的實(shí)際價(jià)值。在后面的文章中我會以這個(gè)例子為基礎(chǔ),在其上面不斷增加一些功能,最終你會看到一個(gè)完整的Demo程序。