最近業余時間在搞h5小游戲,由於同步協議過於頻繁,和服務器之間的同步直接用json就顯得太浪費了,於是我們商討之下決定改用二進制。學習過程中並沒有遇到一篇就解決問題的文章,遂再總結一發。
1.二進制數據的存儲
ArrayBuffer對象、TypedArray對象、DataView對象是JavaScript操作二進制數據的一個接口。
(1)ArrayBuffer對象:代表內存之中的一段二進制數據,它不能直接讀寫,只能通過視圖(TypedArray
視圖和DataView
視圖)來讀寫,視圖的作用是以指定格式解讀二進制數據。
(2) TypedArray對象:ArrayBuffer
對象作為內存區域,可以存放多種類型的數據。同一段內存,不同數據有不同的解讀方式,這就叫做“視圖”(view)。ArrayBuffer
有兩種視圖,一種是TypedArray視圖,另一種是DataView視圖,兩者的區別主要是字節序,前者的數組成員都是同一個數據類型,后者的數組成員可以是不同的數據類型。
(3)DataView對象:用來生成內存的視圖,可以自定義格式和字節序,比如第一個字節是Uint8(無符號8位整數)、第二個字節是Int16(16位整數)、第三個字節是Float32(32位浮點數)等等。
2.把數據寫入二進制數組
例如寫入4個int
var buffer = new ArrayBuffer(16); var int32View = new Int32Array(buffer); for (var i = 0; i < int32View.length; i++) { int32View[i] = i * 2; }
上面代碼生成一個16字節的ArrayBuffer
對象,然后在它的基礎上,建立了一個32位整數的視圖。由於每個32位整數占據4個字節,所以一共可以寫入4個整數,依次為0,2,4,6。
3.大端小端的問題
目前,所有個人電腦幾乎都是小端字節序,所以TypedArray數組內部也采用小端字節序讀寫數據,或者更准確的說,按照本機操作系統設定的字節序讀寫數據。
這並不意味大端字節序不重要,事實上,很多網絡設備和特定的操作系統采用的是大端字節序。
js提供了設置大端和小端的函數,只需要在讀取或寫入時表明即可。dv代表一個ArrayBuffer數組。
例子:
// 小端字節序
var v1 = dv.getUint16(1, true); // 大端字節序
var v2 = dv.getUint16(3, false); // 大端字節序
var v3 = dv.getUint16(3); // 在第1個字節,以大端字節序寫入值為25的32位整數
dv.setInt32(0, 25, false); // 在第5個字節,以大端字節序寫入值為25的32位整數
dv.setInt32(4, 25); // 在第9個字節,以小端字節序寫入值為2.5的32位浮點數
dv.setFloat32(8, 2.5, true);
false或者undefined表示使用大端字節序寫入,true表示使用小端字節序寫入。
4.將二進制數組專為字符串
這里其實被坑了挺久的,我們的協議傳輸之前使用的是JSON.stringify(data)把數據轉成json串進行傳輸,現在需要的數據格式是類似下面這種:
{uid:100, controlData: byteInfo} 其中byteInfo是我們之前寫好的ArrayBuffer,本來以為大功告成,誰知json並不支持二進制數組的數據,轉過之后會無法解析,所以我們還需要把剛才的二進制數據轉為字符串:
// ArrayBuffer轉為字符串,參數為ArrayBuffer對象
function ab2str(buf) { return String.fromCharCode.apply(null, new Uint16Array(buf)); } // 字符串轉為ArrayBuffer對象,參數為字符串
function str2ab(str) { var buf = new ArrayBuffer(str.length * 2); // 每個字符占用2個字節
var bufView = new Uint16Array(buf); for (var i = 0, strLen = str.length; i < strLen; i++) { bufView[i] = str.charCodeAt(i); } return buf; }
5.為了在轉json的時候保證不出問題,最后我們又用了base64,把非ascii字符統一轉為ascii字符
例如:
var encodedData = window.btoa("Hello, world"); // encode a string
var decodedData = window.atob(encodedData); // decode the string
終於大功告成,一個小小的數據經過幾番折騰終於變成了一個可以傳輸的、奇怪的字符串。和服務器調了一發,完美解析
參考資料:
http://javascript.ruanyifeng.com/stdlib/arraybuffer.html 理論基礎,強烈推薦
https://developer.mozilla.org/en-US/docs/Web/API/WindowBase64/atob base64文檔
http://noyesno.net/page/javascript/binary.html
http://stackoverflow.com/questions/6965107/converting-between-strings-and-arraybuffers/