JavaScript二进制数据序列化和反序列化

最近业余时间在搞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/

 

posted @ 2017-01-08 22:34  破晓べ  阅读(10887)  评论(0编辑  收藏  举报