前端也许很少会接触到二进制,至少我没怎么接触过。 之前说的二进制传输,通过设置websocket 的 binaryType = 'arraybuffer',消息下行的时候 onmessage 拿到的 MessageEvent.data 会是一个ArrayBuffer对象,如图:
关于ArrayBuffer,MDN解释: ArrayBuffer对象被用来表示一个通用的,固定长度的二进制数据缓冲区。你不能直接操纵ArrayBuffer的内容;相反,你应该创建一个表示特定格式的buffer的类型化数组对象(typed array objects)或数据视图对象DataView 来对buffer的内容进行读取和写入操作。
类型化数组(typed array objects)有下图这些类型:
实际就是一个ArrayBuffer我们是不能直接操作它的,需要转成可以操作的对象类型,我们是需要转换成Unit8Array,比如这样:
var unit8= new Uint8Array(arrayBuffer);
但是我发现在微信里这样用会报错,在手机默认的浏览器里还是好的,看来还存在一定兼容问题。后来用到DataView才没问题的:
var dataview = new DataView(arrayBuffer); var unit8= new Uint8Array(dataview.buffer, dataview.byteOffset, dataview.byteLength);
兼容问题不止这一点,在phone5测试的时候,一直有问题(同事说那台手机被苹果封过,不晓得会不会和这个有关系),一步步查下去,发现是Unit8Array一些方法在phone5里显示undefined,比如 Unit8Array.slice 和 Unit8Array.from,把 Unit8Array.slice用 Unit8Array.subarray 替换,Unit8Array.from 用 new 替换,像这样:Uint8Array.from([1, 0, 0]) == new Uint8Array([1, 0, 0]),目前来说就没出现其他兼容问题了。
websocket和重连机制:
我们会封装一个独立的websocket类,处理websocket的建立、连接、重连、心跳、监听等,提供一些钩子函数,配合前面说的ImInstance实现业务功能。长连接肯定是会出现断开或者弱网等一系类情况,保证业务的健壮和稳定性,需要做心跳重连。这块之前的博客已经写过,这次项目之后又对代码和博客进行了一些完善,具体可以看之前的博客《初探和实现websocket心跳重连》和心跳的github源码《https://github.com/zimv/WebSocketHeartBeat》。
一些踩到的坑汇总:
下面两个问题有一个知识点: Number类型统一按浮点数处理,64位(bit)存储,整数是按最大54位(bit)来算最大最小数的,否则会丧失精度;某些操作(如数组索引还有位操作)是按32位处理的。
1.位移运算:
每一条消息有个唯一id,id是根据时间戳加上一些其他参数再通过位移运算得出的。 本身根据id可以得出时间,所以就没有专门给时间的字段,这里就需要前端对id进行一次运算,得出时间,但是我在做位移操作的时候发现得出的值不对。 后来才查到了上面的知识点。 server给我们的是64位的int,但是js的位移是按照32位处理的,所以得出的值不对,后来邱桑找到了一个Long.js库,它可以把64位整数拆分成两个32位的去计算,最后我就得到了正确的时间。Long.js
2.number丢失精度:
因为js的整数最大只支持到54bit,范围在 9007199254740992 到 9007199254740992,而我们的id是超过了54bit的(这一点受到了后端同事的疯狂嘲笑)。 在做消息回执(收到一条消息,发送当前消息的id给后端,告知我收到这个消息了)的时候,因为超过了js的最大值,所以前端传出去的id就会是错误的。 比如后端返回了一个id为111111111111111111的值(18个1),前端通过protobuf类解析之后拿到的值直接变成了111111111111111100(16个1加2个0),因为超过了最大值,js用0来占位显示,这样回执给后端的id就是111111111111111100了。 我以为当前存放数字的变量就已经是这个值了,我不管做什么都没用了,那么我希望后端给我一个字符串的id我才好处理(发现这个问题的时候项目正在准备上线),但是邱桑觉得这样多一个字段太浪费。 后来他查了一些资料告诉我,就用Long.js,它可以帮我转换成正确的字符串,我不信,我认为js存不到那么大的数据,js直接把数据给丢失了,而邱桑说值实际还在内存里精度没有丢失,只是js展示不出来,而且非常肯定,我当时不信,在他强烈的要求下,我使用了Long.js的转换方法,结果他是对的。 虽然收到的值超过了js的范围,但是数值仍然是原封不动的在内存里,这个也是被狠狠的打了一下脸,果然还是邱桑厉害! Long.js的代码量还是比较多,当时我想我只用位移就把位移的相关代码抽出来整合了一下,这样比较节约。 后来发现我现在说的这个问题也需要用到Long.js的其它方法,我又尝试抽离,发现要抽的代码太多了,后来干脆就直接把Long.js全部引入进来了(装逼失败)。
3.微信localstorage: