这里记录了使用 protobuf 协议与服务端数据交互的相关内容和知识。涉及到计算机基础知识,例如字节、buffer 缓冲、大小端等。html
1 字节表明了 8 位(bit)二进制,1 位就是 0 或 1,也是计算机最小单位。前端
Int 是带正负号的整数,Uint 是从 0 开始计的整数。git
Uintx
是指用多少位
表示的整数,例如 Uint8
就是用 8位
(即一个字节) 表示的整数,二进制范围是 00000000 ~ 11111111
,对应的十进制就是 0 ~ 255
。github
可是人类的数学里面负数,因此 Int8 就描述了包含负数在内的整数范围,即十进制的 -128 ~ 127
web
更多描述以下所示shell
Uint8 -- (0 to 2^8 - 1) Int8 -- (-2^7 to +2^7 - 1) Uint16 -- (0 to 2^16 - 1) Int16 -- (-2^15 to +2^15 - 1) Uint32 -- (0 to 2^32 - 1) Int32 -- (-2^31 to +2^31) Uint64 -- (0 to 2^64 - 1) Int64 -- (-2^63 to +2^63 - 1)
ArrayBuffer 对象用来表示通用的、固定长度的原始二进制数据缓冲区。参考MDNwebsocket
// 如下为建立 12 个字节的 buffer 的例子 const buffer = new ArrayBuffer(12);
上面的操做表明向操做系统申请了 12 字节的二进制缓冲,大概以下分布socket
| 00000000 | 00000000 | 00000000 | 00000000 | ...(还有8字节)ui
ArrayBuffer 对象并不能直接被操做,须要经过 TypedArray 对象实例或者 DataView 实例做为桥梁来操做。操作系统
// Uint8Array 的单位为一字节与 ArrayBuffer 的基本单位吻合 const uint8 = new Uint8Array(buffer); console.log(uint0) // 输出 [0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0] uint8[0] = 12; // 此时 buffer 变成 [12, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0, 0]
TypedArray 对象一览 MDN
类型 | 大小(字节单位) | 描述 | Web IDL type | |
---|---|---|---|---|
Int8Array | 1 | 8位二进制带符号整数 -2^7~(2^7) - 1 | byte | |
Uint8Array | 1 | 8位无符号整数 0~(2^8) - 1 | octet | |
Int16Array | 2 | 16位二进制带符号整数 -2^15~(2^15)-1 | short | |
Uint16Array | 2 | 16位无符号整数 0~(2^16) - 1 | unsigned short | |
Int32Array | 4 | 32位二进制带符号整数 -2^31~(2^31)-1 | long | |
Uint32Array | 4 | 32位无符号整数 0~(2^32) - 1 | unsigned int | |
Float32Array | 4 | 32位IEEE浮点数 | unrestricted | float |
Float64Array | 8 | 64位IEEE浮点数 | unrestricted | double |
除了 TypedArray,还能够经过 DataView 来作更细致的操做
例如咱们须要在特定字节段内写入对应的数据
| DataLen 4 个字节 | SessionID 8 个字节 | ...
const view = new DataView(buffer); const DataLen = 100; // buffer 数据总长度 const SessionID = 123456789; // SessionID // 最后的参数为大小端排序 view.setUint32(0, DataLen, true); view.setBigUint64(4, BigInt(SessionID), true);
读取内容
const view = new DataView(buffer); // 读取小端字符顺序 const DataLen = view.getUint32(0, true); const SessionID = view.getBigUint64(4, true);
什么是大小端
更多详情参考维基百科的字节顺序
JS 并不能处理 Int64 精度的数,因此在 stage 3
引入了 BigInt
API,解决大数精度问题,Chrome
和 Firefox
已经支持,可是 Safari
并不支持,须要用另外的办法处理。
兼容方式参考 这里
Google Protocol Buffers 是一种轻便高效的结构化数据存储格式,能够用于结构化数据串行化,或者说序列化。
开发时通信双方或者多方终端都遵循 proto 协议。
而后看看前端如何使用 protobuf
Google 官方的库对 JS 支持不是太友好,这里咱们使用 protobuf.js 库
建立一个 sdk.proto 文件
syntax = "proto3"; package yourPackage; message LoginReq { string UserName = 1; string Password = 2; }
yarn add protobufjs -D # 使用 protobufjs 提供的 Command line pbjs ./sdk.proto -t static-module > ./sdk.js # 生成 ts 声明文件 pbts -o ./sdk.d.ts ./sdk.js
生成好文件便可使用
import SDK from './sdk'; const { LoginReq } = SDK.yourPackage; const payload = { UserName: 'alex', Password: '123' } const message = LoginReq.create(payload); // or use .fromObject if conversion is necessary // encode 信息 const protoBuffer = LoginReq.encode(message).finish(); // 把 protobuf buffer 写入到上面的 SessionID buffer 信息中 const uint8 = new Uint8Array(buffer); uint8.set(protoBuffer, offset) // 使用 websocket 发送 arrayBuffer 数据 const socket = new WebSocket(host) socket.onopen = () => { socket.send(protoBuffer) } socket.onmessage = () => { // decode operator }
这里只是简单的记录过程,若是想要更多细节的信息,能够参考 little-chat 的源码