透過 WebSocket 傳送 Binary 資料:ArrayBuffer


這篇算是延續之前的《HTML5 WebSocket Client》和《C++ WebSocket 函式庫:WebSocket++》這兩篇文章,來整理一下,除了傳遞文字資料外,要怎麼透過 WebSocket 來傳遞 Binary 資料。而 client 端基本上還是一般的 JavaScript、Server 端也就還是 WebSocket++ 了~

基本上,WebSocket 在 JavaScript 這端,除了純文字(DOMString),在二進位資料的方面,還支援 BlobArrayBuffer 兩種類型的資料。Blob 基本上是一種沒有特定原生 JavaSctipt 型別的 RAW Data,裡面可以塞各式各樣的東西(參考);而 ArrayBuffer 則是固定寬度的陣列資料,不過由於本身沒有記錄內容的型別,所以必須透過 ArrayBufferView 的物件、來指定內容的型別做存取(參考)。

而 Heresy 這邊,基本上就是使用 ArrayBuffer 來做資料的傳遞 C++ 裡面的陣列。


JavaScript Client

基本上,要在 JavaScript 裡面接收 WebSocket 傳來的 ArrayBuffer 資料,在程式架構上,並不需要做太大的修改;要注意的是,由於  WebSocket 支援的 Binary 資料形式有 BlobArrayBuffer 兩種,所以必須要手動指定要用哪種類型的資料。

而指定的方法,基本上就是在建立出 WebSocket 的物件後,去指定他的 binaryType 的值:

var wWebsocket = new WebSocket(sUri);
wWebsocket.binaryType = "arraybuffer";

他的值只有「arraybuffer」和「blob」兩種,在設定之後,就會以指定的形式,來處理收到的 binary 資料。

而在資料的處理方面,一樣是要在 onmessage 這個事件的 callback function 裡面做處理。基本上的概念,就是先就是要先透過 instanceof 來確認收到的資料是否為需要的型別,然後再轉換成對應的 ArrayBufferView 物件來做使用。像如果接收到的資料是 64bit 的浮點數(double),程式的寫法大致就會像下面這樣:

wWebsocket.onmessage = function (evt) {
  if (evt.data instanceof ArrayBuffer)
  {
    var aDataArray = new Float64Array(evt.data);
    for (var i = 0; i < aDataArray.length; ++i) {
      //aDataArray[i];
    }
  }
};

其中,Float64Array 就是一種 ArrayBufferView 的型別;當把接收到的資料(evt.data)轉換為 Float64Array 的物件(aDataArray)後,就可以把它當作一般的陣列來做存取了~使用上算是相當地方便的。

而除了 Float64Array 這個型別外,還有其他幾種、各自對應到不同類型資料的 ArrayBufferView 型別,基本上如下表(參考):

型別
大小
對應到 C 的型別
Int8Array
1
char
Uint8Array
1
unsigned char
Int16Array
2
short
Uint16Array
2
unsigned short
Int32Array
4
int
Uint32Array
4
unsigned int
Float32Array
4
float
Float64Array
8
double

至於要轉換成哪一種型別,就是要自己和 server 端先做好溝通,搞清楚接下來要傳送的是哪一種類型的資料,才能做進一步的處理了。(這時候真的覺得 asynchronous io 很討厭…)


WebSocket++ Server

上面是 client 端的 JavaScript 程式,而接下來則是 WebSocket++ 的 Server 端程式。

基本上,WebSocket++ 的 endpoint 所提供的 send() 函式,本身就有提供了對應的介面,可以用來傳遞 void*、任何型別的陣列,算是相當方便的。不過由於考慮到 client 端只有提供基礎型別的 ArrayBufferView 型別來做對應,所以這邊還是建議只能來傳 C 內建的基礎型別資料的陣列就好了。

而以要傳遞一個 double 的陣列的話,大致上會是像下面這樣子:

double aData[10];
for( int i = 0; i < 10; ++ i )
  aData[i] = i;
rServer.send( hdl, aData, 10 * sizeof(double),
              websocketpp::frame::opcode::BINARY );

基本上,sned() 所需要的第一個參數,還是一樣是 connection_hdl,用來決定要傳給誰。而第二個參數,而是接受 const void*,用來接受任意型別的陣列;第三個參數,則是要告訴他這個陣列有多大,基本上就是陣列程度、乘上每一項的大小了。最後,則是要告訴 WebSocket++ 是要使二進位的資料,所以指定 opcodeBINARY

而如果要傳遞其他型別的陣列,也只要用同樣的方法,就可以了~


這篇先到這裡,接下來應該是準備來研究怎麼透過 WebSocket 來傳影像了。

廣告

對「透過 WebSocket 傳送 Binary 資料:ArrayBuffer」的想法

  1. 您好,請問透過websocket++ sercer 上,用on_message確認client的所取狀態,並在on_message裡,用迴圈每讀取video的一張frame便傳送一個 data array 給 client一次,可是最後再client(js)上卻發生等待server接收完整個oni的影像才開始顯示,請問有其他方式去傳送影像讓client端可以收到一張便顯示一張?謝謝。

發表迴響

在下方填入你的資料或按右方圖示以社群網站登入:

WordPress.com 標誌

您的留言將使用 WordPress.com 帳號。 登出 /  變更 )

Google photo

您的留言將使用 Google 帳號。 登出 /  變更 )

Twitter picture

您的留言將使用 Twitter 帳號。 登出 /  變更 )

Facebook照片

您的留言將使用 Facebook 帳號。 登出 /  變更 )

連結到 %s

This site uses Akismet to reduce spam. Learn how your comment data is processed.