WebSocket之JS发送二进制

老鼠AI大米_Java全栈

原文:WebSocket之JS发送二进制 - 简书 (jianshu.com)

大家都知道使用socket通信都是二进制,通信框架多是使用二进制通信,高效且快速,但在前端如何编辑发送二进制,二进制数据在日常的JavaScript中很少遇到,但是当你使用WebSocket与后端进行数据交互时,就有可能会用到二进制的数据格式。

JS如何存储和操作二进制

JavaScript中用ArrayBuffer来存储二进制数据。

JavaScript类型化数组将实现拆分为缓冲和视图两部分。一个缓冲(ArrayBuffer)描述的是内存中的一段二进制数据,缓冲没有格式可言,并且不提供机制访问其内容。为了访问在缓存对象中包含的内存,你需要使用视图。视图可以将二进制数据转换为实际有类型的数组。一个缓冲可以提供给多个视图进行读取,不同类型的视图读取的内存长度不同,读取出来的数据格式也不同。缓冲和视图的工作方式如下图所示:

1.png

ArrayBuffer是一个构造函数,可以分配一段可以存放数据的连续内存区域。

var buffer = new ArrayBuffer(8);

上面代码生成了一段8字节的内存区域,每个字节的值默认都是0。1 字节(Byte) = 8 比特(bit),1比特就是一个二进制位(0 或 1)。上面代码生成的8个字节的内存区域,一共有 8*8=64 比特,每一个二进制位都是0。

为了读写这个buffer,我们需要为它指定视图。视图有两种,一种是TypedArray视图,它一共包括9种类型,还有一种是DataView视图,它可以自定义复合类型。 基础用法如下:

var dataView = new DataView(buffer);
dataView.getUint8(0) // 0var int32View = new Int32Array(buffer);
int32View[0] = 1 // 修改底层内存var uint8View = new Uint8Array(buffer);
uint8View[0] // 1

数据类型与字节数

视图类型 说明 字节
Uint8Array 8位无符号整数 1字节
Int8Array 8位有符号整数 1字节
Uint8ClampedArray 8位无符号整数(溢出处理不同) 1字节
Uint16Array 16位无符号整数 2字节
Int16Array 16位有符号整数 2字节
Uint32Array 32位无符号整数 4字节
Int32Array 32位有符号整数 4字节
Float32Array 32位IEEE浮点数 4字节
Float64Array 64位IEEE浮点数 8字节

一个完整的例子:

// 创建一个16字节长度的缓冲
var buffer = new ArrayBuffer(16);
// 创建一个视图,此视图把缓冲内的数据格式化为一个32位(4字节)有符号整数数组
var int32View = new Int32Array(buffer);
// 我们可以像普通数组一样访问该数组中的元素
for (var i = 0; i < int32View.length; i++) {int32View[i] = i * 2;
}
// 运行完之后 int32View 为[0,2,4,6]
// 创建另一个视图,此视图把缓冲内的数据格式化为一个16位(2字节)有符号整数数组
var int16View = new Int16Array(buffer);for (var i = 0; i < int16View.length; i++) {console.log(int16View[i]);
}
// 打印出来的结果依次是0,0,2,0,4,0,6,0

2.png

相信图片已经很直观的表达了这段代码的意思。这里应该有人会疑问,为什么2、4、6这三个数字会排在0的前面,这是因为x86的系统都是使用的小端字节序来存储数据的,小端字节序就是在内存中,数据的高位保存在内存的高地址中,数据的低位保存在内存的低地址中。就拿上面这段代码举例,上图中内存大小排列的顺序是从左向右依次变大,int32View[1]对应的4个字节,它填入的值是 10 (2的2进制表示),把0补齐的话就是 00000000 00000000 00000000 00000010(中间的分隔方便观看),计算机会倒过来填充,最终会成为 00000010 00000000 00000000 00000000。与小端字节序对应的就是大端字节序,它就是我们平时读数字的顺序。

ArrayBuffer

用来表示通用的、固定长度的原始二进制数据缓冲区。
在MDN的文档中,我们能够看到ArrayBuffer的介绍。它是在JavaScript中用来进行二进制数据存储的一种数据对象。

下面我们通过一个示例来简单介绍下ArrayBuffer相关操作。

const buffer = new ArrayBuffer(8);  //8个字节buffer.byteLength; // 结果为8

上面的示例通过创建一个长度为8Byte的二进制数据缓冲区。缓冲区只是一个数据存储的空间,如何对这个存储空间进行读取,完全取决于使用者。例如:8个字节可以当成是2个Int类型的数据,也可以是一个Long类型的数据,或者4个Short型的数据。

DataView

DataView 视图是一个可以从 ArrayBuffer 对象中读写多种数值类型的底层接口,在读写时不用考虑平台字节序问题。

在MDN中关于DataView的介绍。DataView提供了大量的API接口来进行数据的读和写操作。但是,首先我们得先看下说明中提到的字节序问题。

字节序

在现有的计算机体系中,有两种字节序:

  • 大端字节序:高位在前,低位在后。符合人类阅读习惯。
  • 小端字节序:低位在前,高位在后。符合计算机读取习惯。

上面所说的顺序均是针对多字节对象而言,如Int类型,Long类型。以Int类型数据0x1234为例,如果是大端字节序,那么数据从人类对数值的通常写法上来看就是0x1234;如果是小端字节序,那么从人类对数值的通常写法上来看,应该写成0x3412。

对于单字节对象如Byte类型数据而言,没有字节序一说。

在不同的平台中,可能使用不同的字节序,这就是所谓的字节序问题。DataView所谓的在读写时不需要考虑平台字节序问题是指:同时使用DataView进行写入和读取的数据保持一致。

JS数据转二进制

对ArrayBuffer和DataView有了一个大概的了解,下面让我们来看下它是如何进行二进制数据操作的。

let buffer = new ArrayBuffer(6); // 初始化6个Byte的二进制数据缓冲区
let dataView = new DataView(buffer);dataView.setInt16(0, 3); // 从第0个Byte位置开始,放置一个数字为3的Short类型数据(占2 Byte)
dataView.setInt32(2, 15); // 从第2个Byte位置开始,放置一个数字为15的Short类型数据(占4 Byte)

通过上面的示例,我们一共初始化了6个Byte的存储空间,使用1个Short类型(占2 Byte)和一个Int类型(占4 Byte)的数据进行填充。

DataView还提供了许多的API接口来进行其他数据类型的处理,如无符号型,浮点数等。他们的使用方法和上面介绍的API相同,我们在这里就不一一进行介绍了,希望了解更多API接口的读者可以查看MDN文档。

JS中Long类型转换为二进制数据

通过DataView提供的API接口,我们知道了如何处理Short类型、Int类型、Float类型和Double类型。那么,如果是对于Long类型这种原生API中没有提供处理函数的数据类型,我们应该如何处理呢?

首先,我们需要理解Long数据类型的结构,它是由一个高位的4个Byte和低位的4个Byte组成的数据类型。因为Long类型表示的范围比Number类型大,所以我们在JavaScript中是使用了两个Number类型(即Int类型)的对象来表示Long类型数据,相关的具体细节可以见我之前的博客Long.js源码分析与学习。

理解了JavaScript中如何存储Long类型,我们就知道如果对其进行存储。

import Long from 'long';let long = Long.fromString('123');
let buffer = new ArrayBuffer(8);
let dataView = new DataView(buffer);dataView.setInt32(0, long.high); // 采用大端字节序放置
dataView.setInt32(4, long.low);

通过上面的示例,我们将一个Long类型的数据拆分成了两个Int类型的数据,按照大端字节序放入到了ArrayBuffer中。同理,如果是想按照小端字节序放置,只需要将数据进行部分处理后再放入即可,在此我就不过多介绍了。

如何将二进制数据转换为JS中的数据类型

当你知道了如何将数据转换为ArrayBuffer中存储的二进制数据后,就能够简单推测出如何进行反向操作——将数据从ArrayBuffer中读取出来,再转换成JavaScript中常用数据类型。

import Long from 'long';let buffer = new ArrayBuffer(14); // 初始化14个Byte的二进制数据缓冲区
let dataView = new DataView(buffer);
let long = Long.fromString('123');// 数据写入过程
dataView.setInt16(0, 3); // 从第0个Byte位置开始,放置一个数字为3的Short类型数据(占2 Byte)
dataView.setInt32(2, 15); // 从第2个Byte位置开始,放置一个数字为15的Short类型数据(占4 Byte)dataView.setInt32(6, long.high); // 采用大端字节序放置
dataView.setInt32(10, long.low);// 数据读取过程
let shortNumber = dataView.getInt16(0);
let intNumber = dataView.getInt32(2);let longNumber = Long.fromBits(dataView.getInt32(10), dataView.getInt32(6)); // 根据大端字节序读取,该构造函数入参依次为:低16位,高16位

JS中WebSocket使用定制二进制通信

一般情况下,服务端都会定制一套自己的通信协议,如下每个字节定义

  • 第1个字节必须以0xBF开头
  • 第2个字节表示请求类型,如1-request 2-response 3-command
  • 第3个字节开始,写入数据buffer的长度,共占4个字节
  • 第5个字节开始,写入整个buffer

示例代码如下(以protobuf为例):

protobuf.load("proto/MessageDataProto.proto", function (err, root) {// Obtain a message typevar RequestUser = root.lookupType("com.example.nettydemo.protobuf.RequestUser");// Exemplary payloadvar payload = data;// Verify the payload if necessary (i.e. when possibly incomplete or invalid)var errMsg = RequestUser.verify(payload);if (errMsg) {return;}// Create a new messagevar message = RequestUser.create(payload); // or use .fromObject if conversion is necessary// Encode a message to an Uint8Array (browser) or Buffer (node)var buffer = RequestUser.encode(message).finish();let num = 6; //定制协议前部固定长度let len = num+buffer.byteLength;//总字节长度let arrBuffer = new ArrayBuffer(len); // 初始化Byte的二进制数据缓冲区let dataView = new DataView(arrBuffer);//191===0xBFdataView.setInt8(0, 191); // 从第0个Byte位置开始,放置一个数字为3的Short类型数据(占1 Byte)dataView.setInt8(1, 1); //1-request 2-response 3-command//dataView.setInt32(2, 1001); // 从第2个Byte位置开始,放置一个数字为15的Short类型数据(占4 Byte)dataView.setInt32(2, buffer.byteLength); //占4个字节for (var i = 0; i < buffer.byteLength; i++) {dataView.setInt8(num+i, buffer[i]);}if (typeof success === "function") {success(dataView)}if (typeof complete === "function") {complete()}});

小结

通过使用ArrayBuffer和DataView,我们能够快速的将数字数据从二进制转换为JavaScript常用数据类型如Int、Short等;同时,我们也可以将这些数据类型转换为二进制数据或服务端定制的二进制协议。

参考:
http://www.ruanyifeng.com/blog/2016/11/byte-order.html
https://cloud.tencent.com/developer/article/1341898

WebSocket之JS发送二进制相关推荐

  1. Asp.net Core中SignalR Core预览版的一些新特性前瞻,附源码(消息订阅与发送二进制数据)

    前言 一晃一个月又过去了,上个月有个比较大的项目要验收上线.所以忙的脚不沾地.现在终于可以忙里偷闲,写一篇关于SignalR Core的文章了. 先介绍一下SignalR吧,如下: ASP.NET S ...

  2. JS逆向:【硬干货】手把手实战某条_signature参数破解(下)——WebSocket与JS函数

    本文仅供学习交流使用,请勿用于商业用途或不正当行为 如果侵犯到贵公司的隐私或权益,请联系我立即删除 前两篇文章: js逆向:[硬干货]手把手实战某条_signature参数破解(上) js逆向:[硬干 ...

  3. js异步请求php数据,原生JS发送异步数据请求实例详解

    这篇文章主要为大家详细介绍了原生JS发送异步数据请求的相关资料,具有一定的参考价值,感兴趣的小伙伴们可以参考一下 在做项目的时候,有时候需要用到异步数据请求,但是如果这个时候没有框架的依赖,就需要用到 ...

  4. android 上传文件到 c 服务器,android 怎么样用socket给C服务器 发送二进制数据

    最近在写一个小APP,卡在android与服务器端通讯上了,大致需求是,android端发送二进制数据,文件头为0x00 0x08 0x0b,后面跟字符串的内容为abcd_efdgh, 结尾用0x00 ...

  5. JS发送Http请求——AJAX

    1.AJAX的引入 JS 缺乏 主动发起 Http请求 的能力,因此需要代码片段让JS发起Http请求,我们称这样的代码片段为--AJAX(Asynchronous JAvaScript by Xml ...

  6. 原生html如何发送网络请求,原生JS发送HTTP请求的方式:XMLHttpRequest.send()

    在前端开发过程中,就目前来看,发送HTTP请求,大家习惯在框架里去应用,比如vue,react等.我们在往前追朔下,用的最多的是jQuery里的ajax.但是最原始的发送HTTP请求方式是什么呢?一起 ...

  7. html js发送http请求数据格式,JS获取url参数,JS发送json格式的POST请求方法

    一.获取url所有参数值 function US() { var name, value; var str = location.href; var num = str.indexOf("? ...

  8. 使用js发送post请求

    使用js发送post请求  1.先将需要传递的参数封装 2.然后调用提前封装好httpPost方法即可 var params = {"startTime": startTime,& ...

  9. 【js】js发送get请求

    用原生js发送网络请求 var httpRequest = new XMLHttpRequest();//第一步:建立所需的对象httpRequest.open('GET', 'url', true) ...

最新文章

  1. 005,使用Java客户端连接RabbitMQ,构造我们的第一个Publish和Consumer应用
  2. 架构中的一切都是权衡
  3. 手把手教你用Python进行回归(附代码、学习资料)
  4. asp.net confirm提示
  5. 移动端网站优化也不容小觑!
  6. 【编程题目】输入一个单向链表,输出该链表中倒数第 k 个结点
  7. oracle raw性能,对Oracle Raw类型的详细解剖
  8. CSS的特性之层叠性介绍
  9. Linux(centos)的常用基本命令
  10. activemq和jms_带有ActiveMQ和Maven的JMS Sender应用程序
  11. Intel PAUSE指令变化影响到MySQL的性能,该如何解决?
  12. 错误记录,找不到sqlite dll
  13. coreboot学习9:ramstage阶段之设备初始化流程
  14. err=etherbase address must be explicitly specified
  15. css 多行文本的溢出显示省略号(移动端)
  16. 多种CAML条件的查询
  17. H5与SCC3的新特性
  18. 阿里云域名注册+网站备案
  19. javaScript数组操作--有道笔记整理
  20. 如何处理json数据

热门文章

  1. android ndk配置环境,在Windows中的安装Android NDK开发环境
  2. 麦克纳姆轮平台坐标系说明
  3. 麦克纳姆轮平台的两种构型
  4. java jtextfield 输入_【java】JTextField与JComboBox结合动态匹配输入信息
  5. mongodb 可视化_自动爬取疫情数据、交互式地图可视化
  6. 绝对定位水平垂直居中(HTML、CSS)
  7. CSS基本选择器之类选择器多类名(CSS、HTML)
  8. 实验2-2-8 阶梯电价 (15 分)
  9. 【CSS】Table样式
  10. Backup Volume 操作 - 每天5分钟玩转 OpenStack(59)