最近的几篇文章中都涉及到了Buffer
, 虽然用过几个API, 仍是系统总结一下, 主要针对API, 具体使用会少一点web
为何使用Buffer
?bash
在引入 TypedArray 以前,JavaScript 语言没有用于读取或操做二进制数据流的机制。 Buffer 类是做为 Node.js API 的一部分引入的,用于在 TCP 流、文件系统操做、以及其余上下文中与八位字节流进行交互。网络
最经常使用的场景在于:函数
TCP
流: 目前遇到用在封包和解包阶段, 提到的三篇文章中都有涉及源码分析
WebSocket
数据帧RPC
通信协议文件系统操做学习
参考ui
单纯建立大小为size
字节的新Buffer
Buffer.alloc(size[, fill[, encoding]])
分配的buffer
会经过buf.fill
进行初始化, 能确保永远不会包含敏感数据
由于有初始化操做, 所以速度相较于allocUnsafe
慢得多
Buffer.allocUnsafe(size)
buffer
内存是未初始化的, 可能包含敏感数据Buffer.allocUnsafeSlow(size)
分配的buffer
内存是未初始化的, 可能包含敏感数据
建立一个非内存池的buffer
, 避免垃圾回收机制因建立太多独立的buffer
而过分使用
三者选择:
// 须要一个全新的buffer
Buffer.alloc
// 须要一个buffer, 不在乎有没有敏感数据, 反正都会被覆盖, 更经常使用
Buffer.allocUnsafe
// 须要一个须要再内存池中保留的buffer
Buffer.allocUnsafeSlow
复制代码
Buffer.from(string[, encoding])
buffer
, 是最经常使用的Buffer.from(buffer)
buffer
建立一个buffer
, 经常使用于复制Buffer.from(array)
buffer
, 若是不是数字, 自动被0填充Buffer.from(arrayBuffer[, byteOffset[, length]])
: 由于没用过, 记了也没价值
Buffer.from(object[, offsetOrEncoding[, length]])
从对象建立一个buffer
, 实际相似于Buffer.from(string)
本质上是调用对象的Symbol.toPrimitive
或valueOf
方法
栗子1: Buffer.from(object)
class A {
[Symbol.toPrimitive]() {
return 'A'
}
}
Buffer.from(new A(), 'utf-8')
// 打印 <Buffer 41>
复制代码
栗子2: Buffer.from(array)
Buffer.from(['a', '1', 2, 'b', '3'])
// 打印 <Buffer 00 01 02 00 03>
复制代码
Buffer
实例可使用for..of
语法迭代, 相关的函数还有:
buf.keys()
buf.values()
buf.entries()
不过这样很是无聊
const buf = Buffer.from([1, 2, 3]);
for (const b of buf) {
console.log(b);
}
// 打印:
// 1
// 2
// 3
复制代码
索引操做符都比这有趣: 索引操做符 [index] 可用于获取或设置 buf 中指定位置的字节
const buf = Buffer.from('Calabash')
console.log(buf[0]) // 67
复制代码
buf.compare
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
buf
中的某个区域的数据到target
中的某个区域buf.equals(otherBuffer)
buffer
是否具备彻底相同的字节Buffer.concat(list[, totalLength])
Buffer
数组考虑一个w位(bit)的整数,位表示为[Xw-1, Xw-2, ... ,X1, X0],其中Xw-1是最高有效位, 而X0是最低有效位。假设w是8的倍数,这些位就能被分组成为字节,其中最高有效字节包含位[Xw-1, Xw-2, ... ,Xw-8], 而最低有效字节包含位[X7, X6, ... ,Xw-8], 大小端指的就是有效字节的排列方式
小端法(little endian): 最低有效字节在最前面
大端法(big endian): 最高有效字节在最前面
大端 vs 小端对比
// 假设后面两个字节二进制值为
1111 1111 0000 0001
// 转为十六进制为
0xff 0x01
// 大端输出 65281
console.log(Buffer.from([0xff, 0x01]).readUInt16BE(0).toString(10))
// 小端输出 511
console.log(Buffer.from([0xff, 0x01]).readUInt16LE(0).toString(10))
复制代码
TCP/IP为任意整数数据项定义了统一的网络字节顺序(network byte order):大端字节顺序
额外补充一点~
网络序就是大端法字节顺序
本地序依据机器类型,多是大端法字节顺序或者小端法字节顺序
读字节流一共22个API, 其中包含以下两个只须要一个字节就能表示, 不涉及大小端的API
buf.readInt8
: 读取一个有符号的8位整数, 1个字节
buf.readUint8
: 读取一个无符号的8位整数, 1个字节
其余的数字类型包括:
读取BigInt
类型, 8个字节:
readBigInt64BE
readBigInt64LE
readBigUInt64BE
readBigUInt64LE
读取64位双精度浮点数, 8个字节:
readDoubleBE
readDoubleLE
读取32位浮点数, 4个字节:
readFloatBE
readFloatLE
读取16位整数, 2个字节
readInt16BE
readInt16LE
readUInt16BE
readUInt16LE
读取32位整数, 4个字节
readInt32BE
readInt32LE
readUInt32BE
readUInt32LE
读取指定的字节数, 最高支持48位精度, 0~6个字节
readIntBE
readIntLE
readUIntBE
readUIntLE
写字节流的API与上方一致, 只须要将read
换成write
一共涉及三个方法, 都会将buf
解析成无符号的x位整数的数组, 并以字节的顺序原地进行交换
swap16()
: buf.length
必须是2的倍数
swap32()
: buf.length
必须是4的倍数
swap64()
: buf.length
必须是8的倍数
例如:
// 65281
Buffer.from([0xff, 0x01]).readUInt16BE(0).toString(10)
// 511
Buffer.from([0xff, 0x01]).readUInt16LE(0).toString(10)
// 511
Buffer.from([0xff, 0x01]).swap16().readUInt16BE(0).toString(10)
复制代码
buf.toJSON
: 返回buf
的JSON格式, 当字符串化Buffer
时, JSON.stringify()
会调用该函数const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
const json = JSON.stringify(buf);
console.log(json);
// 打印: {"type":"Buffer","data":[1,2,3,4,5]}
复制代码
buf.slice(start, end)
: 建立一个指向与原始Buffer
同一内存得新Buffer
, 但进行了裁剪, 注意两个对象所分配的内存是重叠的, 修改一个会影响另外一个
base64
和string
的转换
// Y2FsYWJhc2g=
const base64Str = Buffer.from('calabash').toString('base64')
// calabash
Buffer.from(base64Str, 'base64').toString()
复制代码
到这里基本完成Buffer API
的学习, 不过以我目前的水平, 没有须要本身封包解包的场景, 只有看别人封包解包了TAT