websocket探索其與語音、圖片的能力

發表於2015-12-26

說到websocket想比大家不會陌生,如果陌生的話也沒關係,一句話概括

“WebSocket protocol 是HTML5一種新的協議。它實現了瀏覽器與伺服器全雙工通訊”

WebSocket相比較傳統那些伺服器推技術簡直好了太多,我們可以揮手向comet和長輪詢這些技術說拜拜啦,慶幸我們生活在擁有HTML5的時代~

這篇文章我們將分三部分探索websocket

首先是websocket的常見使用,其次是完全自己打造伺服器端websocket,最終是重點介紹利用websocket製作的兩個demo,傳輸圖片和線上語音聊天室,let’s go

一、websocket常見用法

這裡介紹三種我認為常見的websocket實現……(注意:本文建立在node上下文環境

1、socket.io

先給demo

相信知道websocket的同學不可能不知道socket.io,因為socket.io太出名了,也很棒,它本身對超時、握手等都做了處理。我猜測這也是實現websocket使用最多的方式。socket.io最最最優秀的一點就是優雅降級,當瀏覽器不支援websocket時,它會在內部優雅降級為長輪詢等,使用者和開發者是不需要關心具體實現的,很方便。

不過事情是有兩面性的,socket.io因為它的全面也帶來了坑的地方,最重要的就是臃腫,它的封裝也給資料帶來了較多的通訊冗餘,而且優雅降級這一優點,也伴隨瀏覽器標準化的進行慢慢失去了光輝

Chrome Supported in version 4+
Firefox Supported in version 4+
Internet Explorer Supported in version 10+
Opera Supported in version 10+
Safari Supported in version 5+

在這裡不是指責說socket.io不好,已經被淘汰了,而是有時候我們也可以考慮一些其他的實現~

 

2、http模組

剛剛說了socket.io臃腫,那現在就來說說便捷的,首先demo

很簡單的實現,其實socket.io內部對websocket也是這樣實現的,不過後面幫我們封裝了一些handle處理,這裡我們也可以自己去加上,給出兩張socket.io中的原始碼圖

 

3、ws模組

後面有個例子會用到,這裡就提一下,後面具體看~

 

二、自己實現一套server端websocket

剛剛說了三種常見的websocket實現方式,現在我們想想,對於開發者來說

websocket相對於傳統http資料互動模式來說,增加了伺服器推送的事件,客戶端接收到事件再進行相應處理,開發起來區別並不是太大啊

那是因為那些模組已經幫我們將資料幀解析這裡的坑都填好了,第二部分我們將嘗試自己打造一套簡便的伺服器端websocket模組

感謝次碳酸鈷的研究幫助,我在這裡這部分只是簡單說下,如果對此有興趣好奇的請百度【web技術研究所】

自己完成伺服器端websocket主要有兩點,一個是使用net模組接受資料流,還有一個是對照官方的幀結構圖解析資料,完成這兩部分就已經完成了全部的底層工作

首先給一個客戶端傳送websocket握手報文的抓包內容

客戶端程式碼很簡單

伺服器端要針對這個key驗證,就是講key加上一個特定的字串後做一次sha1運算,將其結果轉換為base64送回去

這樣握手部分就已經完成了,後面就是資料幀解析與生成的活了

先看下官方提供的幀結構示意圖

簡單介紹下

FIN為是否結束的標示

RSV為預留空間,0

opcode標識資料型別,是否分片,是否二進位制解析,心跳包等等

給出一張opcode對應圖

MASK是否使用掩碼

Payload len和後面extend payload length表示資料長度,這個是最麻煩的

PayloadLen只有7位,換成無符號整型的話只有0到127的取值,這麼小的數值當然無法描述較大的資料,因此規定當資料長度小於或等於125時候它才作為資料長度的描述,如果這個值為126,則時候後面的兩個位元組來儲存資料長度,如果為127則用後面八個位元組來儲存資料長度

Masking-key掩碼

下面貼出解析資料幀的程式碼

然後是生成資料幀的

都是按照幀結構示意圖上的去處理,在這裡不細講,文章重點在下一部分,如果對這塊感興趣的話可以移步web技術研究所~

 

三、websocket傳輸圖片和websocket語音聊天室

正片環節到了,這篇文章最重要的還是展示一下websocket的一些使用場景

1、傳輸圖片

我們先想想傳輸圖片的步驟是什麼,首先伺服器接收到客戶端請求,然後讀取圖片檔案,將二進位制資料轉發給客戶端,客戶端如何處理?當然是使用FileReader物件了

先給客戶端程式碼

接收到訊息,然後readAsDataURL,直接將圖片base64新增到頁面中

轉到伺服器端程式碼

注意s.push((1 << 7) + 2)這一句,這裡等於直接把opcode寫死了為2,對於Binary Frame,這樣客戶端接收到資料是不會嘗試進行toString的,否則會報錯~

程式碼很簡單,在這裡向大家分享一下websocket傳輸圖片的速度如何

測試很多張圖片,總共8.24M

普通靜態資源伺服器需要20s左右(伺服器較遠)

cdn需要2.8s左右

那我們的websocket方式呢??!

答案是同樣需要20s左右,是不是很失望……速度就是慢在傳輸上,並不是伺服器讀取圖片,本機上同樣的圖片資源,1s左右可以完成……這樣看來資料流也無法衝破距離的限制提高傳輸速度

下面我們來看看websocket的另一個用法~

 

用websocket搭建語音聊天室

先來整理一下語音聊天室的功能

使用者進入頻道之後從麥克風輸入音訊,然後傳送給後臺轉發給頻道里面的其他人,其他人接收到訊息進行播放

看起來難點在兩個地方,第一個是音訊的輸入,第二是接收到資料流進行播放

先說音訊的輸入,這裡利用了HTML5的getUserMedia方法,不過注意了,這個方法上線是有大坑的,最後說,先貼程式碼

第一個引數是{audio: true},只啟用音訊,然後建立了一個SRecorder物件,後續的操作基本上都在這個物件上進行。此時如果程式碼執行在本地的話瀏覽器應該提示你是否啟用麥克風輸入,確定之後就啟動了

接下來我們看下SRecorder建構函式是啥,給出重要的部分

AudioContext是一個音訊上下文物件,有做過聲音過濾處理的同學應該知道“一段音訊到達揚聲器進行播放之前,半路對其進行攔截,於是我們就得到了音訊資料了,這個攔截工作是由window.AudioContext來做的,我們所有對音訊的操作都基於這個物件”,我們可以通過AudioContext建立不同的AudioNode節點,然後新增濾鏡播放特別的聲音

錄音原理一樣,我們也需要走AudioContext,不過多了一步對麥克風音訊輸入的接收上,而不是像往常處理音訊一下用ajax請求音訊的ArrayBuffer物件再decode,麥克風的接受需要用到createMediaStreamSource方法,注意這個引數就是getUserMedia方法第二個引數的引數

再說createScriptProcessor方法,它官方的解釋是:

Creates a ScriptProcessorNode, which can be used for direct audio processing via JavaScript.

——————

概括下就是這個方法是使用JavaScript去處理音訊採集操作

終於到音訊採集了!勝利就在眼前!

接下來讓我們把麥克風的輸入和音訊採集相連起來

context.destination官方解釋如下

The destination property of the AudioContext interface returns an AudioDestinationNoderepresenting the final destination of all audio in the context.

——————

context.destination返回代表在環境中的音訊的最終目的地。

好,到了此時,我們還需要一個監聽音訊採集的事件

audioData是一個物件,這個是在網上找的,我就加了一個clear方法因為後面會用到,主要有那個encodeWAV方法很贊,別人進行了多次的音訊壓縮和優化,這個最後會伴隨完整的程式碼一起貼出來

此時整個使用者進入頻道之後從麥克風輸入音訊環節就已經完成啦,下面就該是向伺服器端傳送音訊流,稍微有點蛋疼的來了,剛才我們說了,websocket通過opcode不同可以表示返回的資料是文字還是二進位制資料,而我們onaudioprocess中input進去的是陣列,最終播放聲音需要的是Blob,{type: ‘audio/wav’}的物件,這樣我們就必須要在傳送之前將陣列轉換成WAV的Blob,此時就用到了上面說的encodeWAV方法

伺服器似乎很簡單,只要轉發就行了

本地測試確實可以,然而天坑來了!將程式跑在伺服器上時候呼叫getUserMedia方法提示我必須在一個安全的環境,也就是需要https,這意味著ws也必須換成wss……所以伺服器程式碼就沒有采用我們自己封裝的握手、解析和編碼了,程式碼如下

程式碼還是很簡單的,使用https模組,然後用了開頭說的ws模組,userMap是模擬的頻道,只實現轉發的核心功能

使用ws模組是因為它配合https實現wss實在是太方便了,和邏輯程式碼0衝突

https的搭建在這裡就不提了,主要是需要私鑰、CSR證照籤名和證照檔案,感興趣的同學可以瞭解下(不過不了解的話在現網環境也用不了getUserMedia……)

下面是完整的前端程式碼

注意:按住a鍵說話,放開a鍵傳送

自己有嘗試不按鍵實時對講,通過setInterval傳送,但發現雜音有點重,效果不好,這個需要encodeWAV再一層的封裝,多去除環境雜音的功能,自己選擇了更加簡便的按鍵說話的模式

 

這篇文章裡首先展望了websocket的未來,然後按照規範我們自己嘗試解析和生成資料幀,對websocket有了更深一步的瞭解

最後通過兩個demo看到了websocket的潛力,關於語音聊天室的demo涉及的較廣,沒有接觸過AudioContext物件的同學最好先了解下AudioContext

文章到這裡就結束啦~有什麼想法和問題歡迎大家提出來一起討論探索~

 

相關文章