字节序
概览
本文讲解了火山引擎语音合成中 WebSocket 报文组装的技术细节,官方文档没有 nodejs 的 Demo。
同时,了解字节序方便前端开发掌握组装服务端通信的数据包,以及处理服务端响应的二进制格式数据。
编码与字符集
在日常的开发中,文件的编码格式是 utf-8,也就是我们书写的代码在文件中的存储编码格式。
在 js 执行过程中,字符的编码格式是 utf-16,这样能存储、解析更多的字符。
所以,在 js 中解码字符的格式就是 utf-16,由2个字节组成(在unicode字符集的基本平面内的字符)。
ascii 字符集是单子节字符,最多表示128个字符,是 unicode 字符集的子集。
unicode 字符集单个字符的码点范围是 0000-FFFF,用二进制表示的话就是 2 个字节。
这2个字节的顺序也称为字节序,只不过是 utf-16 的字节序。
字节序
当一个字符由多个字节组成时,就会存在字节顺序的问题。
顺序即:从左到右,也就是从高到低(进制的下标索引决定)。
小端序(Little Endian):也就是从高字节放最高数据,低字节放低数据,顺序符合从高到低。
大端序(Big Endian):是反的,但是符合人的从左到右阅读的习惯,和数组的索引顺序一致。
进制数表示
0x11
是十六进制数的表示方式,而 0b00010001
是二进制数的表示方式。
0x11
表示的是十六进制数,其中0x
前缀表示其后的数字是十六进制数。十六进制数可以使用 0-9 和 A-F(或者 a-f)表示,其中 A-F 分别对应十进制数 10-15。因此,0x11
表示十进制数 17。0b00010001
表示的是二进制数,其中0b
前缀表示其后的数字是二进制数。二进制数由 0 和 1 组成,表示的是计算机中的位。因此,0b00010001
表示十进制数 17。
字节序实践
火山引擎语音合成对接,websocket 报文参数文档如下
以大端序存储,解析:
- 横着看,7、6、5、4、3、2、1、0,表示字节索引序列,即高位存低数据。
- 竖着看,0、1、2、3、4、5、6 表示报文字段的存储顺序
发送报文的参数定义
那么该报文的二进制书写为:
- protocol version: 0001
- header size: 0001
- message type 0001
- message type specific flags: 0000
- message serialization method: 0001
- message compression: 0000,不压缩
- reserved 是边界,固定是0,使整个报头大小为4个字节
使用 nodejs 书写:
jsx
// 0001 0001
// 0001 0000
// 0001 0000
// 0000 0000
const payloadHeader = Buffer.from([
0b00010001,
0b00010000,
0b00010000,
0b00000000
])
// 一般用 16进制表示
const payloadHeader = Buffer.from([0x11, 0x10, 0x10, 0x00])
整个报文的完整代码
jsx
const defaultHeader = Buffer.from([0x11, 0x10, 0x10, 0x00])
// 报文消息体
const payloadBytes = Buffer.from(JSON.stringify(data))
// 完整报文
const fullClientRequest = Buffer.alloc(defaultHeader.length + 4 + payloadBytes.length)
// 将 header 拷贝到报文
defaultHeader.copy(fullClientRequest)
// 将消息体长度的数值写入报文,并偏移 header 的长度
fullClientRequest.writeUInt32BE(payloadBytes.length, defaultHeader.length)
// 将消息体拷贝进报文,并偏移
payloadBytes.copy(fullClientRequest, defaultHeader.length + 4)
这里有2个注意点:
- 报文长度最长为4个字节,0x2ffff,还是挺长的
- 报文长度写入使用的是 writeUInt32BE,Uint32,因为是4个字节