内容简介:最近的几篇文章中都涉及到了
最近的几篇文章中都涉及到了 Buffer
, 虽然用过几个API, 还是系统总结一下, 主要针对API, 具体使用会少一点
为什么使用 Buffer
?
在引入 TypedArray 之前,JavaScript 语言没有用于读取或操作二进制数据流的机制。 Buffer 类是作为 Node.js API 的一部分引入的,用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。
最常用的场景在于:
-
TCP
流: 目前遇到用在封包和解包阶段, 提到的三篇文章中都有涉及WebSocket RPC
-
文件系统操作
参考
1. 创建Buffer
1.1 alloc家族
单纯创建大小为 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 复制代码
1.2 from五胞胎
-
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> 复制代码
2. Buffer的迭代
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 复制代码
3. 两个buffer间的碰撞
-
buf.compare
- 主要用于 Buffer 数组的排序, 建议跳过
-
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])
-
拷贝
buf
中的某个区域的数据到target
中的某个区域
-
拷贝
-
buf.equals(otherBuffer)
-
比较两个
buffer
是否具有完全相同的字节
-
比较两个
-
Buffer.concat(list[, totalLength])
-
合并list中的
Buffer
数组
-
合并list中的
4. BE和LE世家
4.1 大小端
考虑一个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):大端字节顺序
额外补充一点~
-
网络序就是大端法字节顺序
-
本地序依据机器类型,可能是大端法字节顺序或者小端法字节顺序
4.2 读/写字节流
读字节流一共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
4.3 交换大小端
一共涉及三个方法, 都会
将 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) 复制代码
5. 小甜点
-
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() 复制代码
6. END
到这里基本完成 Buffer API
的学习, 不过以我目前的水平, 没有需要自己封包解包的场景, 只有看别人封包解包了TAT
以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网
猜你喜欢:- Nginx中常用的模块整理
- 月薪 50K 大牛整理!六大Java架构进阶技术模块,看完茅塞顿开!
- web系列之模块化——AMD、CMD、CommonJS、ES6 整理&&比较
- 照片整理系列二 —— 照片整理及归档的辛酸历程
- 我自己整理的码农周刊一周分享整理
- 【复习资料】ES6/ES7/ES8/ES9资料整理(个人整理)
本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们。