前言

  之前写WebSocket都是基于文本传输的,后来准备升级项目,于是打算尝试一下arraybuffer传输方式,由于是第一次使用javascript处理字符串转arraybuffer,不过真的是一把辛酸泪啊,特此记录。

项目背景

  还是之前写的(基于Tio通讯框架的SpringBootLayIM项目)[https://github.com/fanpan26/SpringBootLayIM].

开发过程还原

  • 1.要将客户端的传输方式改为arraybuffer
     var ws = new WebSocket(tool.options.server + '?access_token=' + token);
ws.binaryType = 'arraybuffer';
  • 2.我们就要用到框架中的 IWsMsgHandler.onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext)方法接收客户端的消息
    /**
* 字节传输
* */
public Object onBytes(WsRequest wsRequest, byte[] bytes, ChannelContext channelContext) throws Exception {
System.out.println("接收到的消息为:"+new String(bytes));
return null;
}
  • 3.在ws.send之前要先将发送的内容转换。
  //
var buff = new TextEncoder().encode(str);

  很好,这三个步骤做完之后,我们试一下。可以看到,打印结果正常

接收到的消息为:{"username":"雍正王","avatar":"/static/images/demo/huangshang.jpg","id":203328,"type":"friend","content":"22"}

  到此为止呢,还是没有任何问题的。按照我之前的思路就是将消息反序列化成对象实例,然后进行相应的逻辑操作。不过呢,后来突发奇想,本来我传递过来的消息就是想原封不动的发送给对方,也就是说程序不需要执行反序列化的过程。那可不可以这样,如下图:



没错就是这样子,然后我打印了一下new TextEncoder().encode(str);返回的内容如下:



  可以看到,它是一个 Uint8Array,于是我就看看能否重新构造一个数组,用伪代码实现就是酱紫:

var newBuff = new ArrayBuffer(4+1+bodyArray.buffer.byteLength)

再查资料发现ArrayBuffer得依赖DataView来进行赋值操作,那不就简单啦。先把接收人ID(UInt32)和 消息类型 (UInt8) 写入,如下:

  var view = new DataView(dataBuff.buffer);
view.setInt32(0, targetId);
view.setInt8(4, 1);

那剩下的body怎么写进去呢?按照我想的,应该是有个 setContent(arr,offset:number)方法,可是查了一下,没有。于是乎再查文档,原来Unit8Array有个遍历的方法,那就用这个方法试试吧。

    //从索引第五个开始写
dataBuff.forEach(function (value, index) {
view.setInt8(5+index,value);
});

为了验证我这个思路(呜呜,这个思路还是研究了好久,浪费了很多时间。。。其实中间由于不熟悉DataView和ArrayBuffer导致做了很多尝试的工作,而且都失败了,要么就是报错,要么就是覆盖了消息体)的正确性,我们将后台代码改一下:

       byte[] targetIdBytes = Arrays.copyOf(bytes,4);
byte[] contents = Arrays.copyOfRange(bytes,5,bytes.length);
System.out.println("消息体:"+new String(contents));
int targetId = ConvertUtil.byteArrayToInt(targetIdBytes);
System.out.println("接收人:"+targetId);
System.out.println("消息类型"+bytes[4]);

运行正常。



可是,总觉得在遍历复制一遍有点繁琐。那既然setInt32,setUInt8这些方法是覆盖数组里的值的,那我可不可以这样写呢?使用占位符的方式,也就是说,在不影响消息体的情况下,在转化成byte数组之后,前五位是占位数据,后边才是正确的消息,那么我重写前五位的内容也不会受到什么影响了,而且不用遍历赋值了。说干就干,改成代码如下:

   var str = placeholder + JSON.stringify(d);
var buff = new TextEncoder().encode(str);
return buff;

问题就在于这个placeholder的值是什么呢?其实我们使用小写字母代替就可以了。比如 'abcde',看一下转换的结果:



不出我所料,那这样的话,就没问题啦,直接覆盖前五位就可以了。客户端完整代码如下:

  //根据layim提供的data数据,进行解析
var mine = data.mine,
to = data.to,
id = mine.id,
group = to.type === 'group';
if (group) {
id = to.id;
}
//构造消息
var msg = {
username: mine.username
, avatar: mine.avatar
, id: id
, type: to.type
, content: mine.content
}, targetId = to.id var dataBuff = this.encode(msg);
var view1 = new DataView(dataBuff.buffer);
view1.setInt32(0, targetId);
view1.setInt8(4, group ? msgType.chatGroup : msgType.chatFriend);
return view1.buffer;

运行一下,结果没问题,大功告成!



总结

  从问题的发出到解决,虽然从博客上来看没有什么难度,但是自己在做的时候,搜了很多资料,尝试了很多次也没有结果,原因是自己自动脑补了一些对象的操作API,基础知识还是很重要的啊,滚回去学习。虽然最终的思路不一定是最好的,或者还有一些其他的问题,但是多尝试一下还是有很多意外的收获。那么问题来了。反序列化和数组的拷贝哪个效率更高一些呢?

  //这个好还是
byte[] targetIdBytes = Arrays.copyOf(bytes,4);
byte[] contents = Arrays.copyOfRange(bytes,5,bytes.length); //这个好呢?
Json.toBean(new String(contents))

折腾一下WebSocket的ArrayBuffer传输方式的更多相关文章

  1. 旧文备份:CANopen协议PDO的几种传输方式

    (于2007.1.22) 由于PDO所传输的数据内容是无协议的且分配的标识符范围较SDO靠前,因此,其效率和优先级都是较高的,通常用于实时过程数据的传输. PDO是生产/消费类型的通讯方式,数据只有一 ...

  2. ZeroMQ接口函数之 :zmq_inproc – ØMQ 本地进程内(线程间)传输方式

    ZeroMQ API 目录 :http://www.cnblogs.com/fengbohello/p/4230135.html ——————————————————————————————————— ...

  3. Linux 上的常用文件传输方式介绍与比较

    ftp ftp 命令使用文件传输协议(File Transfer Protocol, FTP)在本地主机和远程主机之间或者在两个远程主机之间进行文件传输. FTP 协议允许数据在不同文件系统的主机之间 ...

  4. Android通过http协议POST传输方式

    Android通过http协议POST传输方式如下: 方式一:HttpPost(import org.apache.http.client.methods.HttpPost) 代码如下: privat ...

  5. read/write数据读写传输方式(转)

    前言 笔者本打算撰写一篇讲解标准I/O(缓存I/O)的博文,但是发现已经有网友做过同样的工作,并且工作质量上乘,特转载于此. 原文地址http://lenky.info/archives/2012/0 ...

  6. ftp两种传输方式区别

    转自:http://linux.chinaitlab.com/server/806269.html FTP可用多种格式传输文件,通常由系统决定,大多数系统(包括UNIX系统)只有两种模式:文本模 式和 ...

  7. ftp二进制与ascii传输方式区别

    ASCII 和BINARY模式区别:    用HTML 和文本编写的文件必须用ASCII模式上传,用BINARY模式上传会破坏文件,导致文件执行出错.    BINARY模式用来传送可执行文件,压缩文 ...

  8. Linux上常用的文件传输方式以及比较

    tp ftp 命令使用文件传输协议(File Transfer Protocol, FTP)在本地主机和远程主机之间或者在两个远程主机之间进行文件传输. FTP 协议允许数据在不同文件系统的主机之间传 ...

  9. java 实现websocket的两种方式

    简单说明 1.两种方式,一种使用tomcat的websocket实现,一种使用spring的websocket 2.tomcat的方式需要tomcat 7.x,JEE7的支持. 3.spring与we ...

随机推荐

  1. HTTP2 概述

    HTTP/2,也就是超文本传输协议第2版,不论是1还是2,HTTP的基本语义是不变的,比如方法语义(GET/PUST/PUT/DELETE),状态码(200/404/500等),Range Reque ...

  2. 解决:IDEA 中 new Java Class 怎么没了?

    前言:写代码时遇到的问题,所以记录下来.我的包名为“interface”,只有这个包及包下的文件不能建java文件. 问题 解决方式(对于普通包名) 点击应用,ok就可以了. 解决方式(对于包名为“i ...

  3. spss C# 二次开发 学习笔记(六)——Spss统计结果的输出

    Spss的二次开发可以很简单,实例化一个对象,然后启用服务,接着提交命令,最后停止服务. 其中重点为提交命令,针对各种统计功能需求,以及被统计分析的数据内容等,命令的内容可以很复杂,但也可以简单的为一 ...

  4. cookie、session、分页

    一.cookie HTTP协议是无状态的. 无状态的意思是每次请求都是独立的,它的执行情况和结果与前面的请求和之后的请求都无直接关系,它不会受前面的请求响应情况直接影响,也不会直接影响后面的请求响应情 ...

  5. jQuery处理JSONP

    http://www.g7blogs.com/?p=821 作为一枚前端,提起jsonp大家都不会陌生.特别是在我们组内的业务中,和服务器端交互的数据几乎都是采用这种形式.但假如要让你用原生的JS写出 ...

  6. JavaWeb学习总结(五):HttpServletRespone对象(一)

    Web服务器收到客户端的http请求,会针对每一次请求,分别创建一个用于代表请求的request对象.和代表响应的response对象.request和response对象即然代表请求和响应,那我们要 ...

  7. js笔记 -- toString() 和String()

    将一个值转换成一个字符串有两种方法,一是使用toString()方法,二是使用转型函数String().下面是一些需要注意的问题: 1,大多值都有toString()方法,因为toString是Obj ...

  8. 用SumatraPdf实现PDF静默打印

    原文:https://yutuo.net/archives/24a22d50d6001564.html 之前做一系统,有一个打印PDF文件的需求,需求如下: 能指定打印机 能两面打印 最好能静默打印 ...

  9. 在 ubuntu 上运行 php 脚本

    在 ubuntu 上运行 php 脚本 一.配置运行环境 1.要在 ubuntu 上运行 php 脚本,需要安装 Apache2 和 PHP 具体步骤,请参考:Ubuntu 搭建Web服务器(MySQ ...

  10. 安卓app开发-05-Android xml布局详细介绍

    安卓app开发-05-Android xml布局详细介绍 虽然说有 墨刀,墨客 这些图形化开发工具来做 Android 的界面设计,但是我们还是离不开要去学习做安卓原生app,学习 xml 布局还是必 ...