最近业余时间在搞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://*.com/questions/6965107/converting-between-strings-and-arraybuffers/