Node.js - Buffer模块API整理

栏目: Node.js · 发布时间: 5年前

内容简介:最近的几篇文章中都涉及到了

最近的几篇文章中都涉及到了 Buffer , 虽然用过几个API, 还是系统总结一下, 主要针对API, 具体使用会少一点

Remote Procedure Call

WebSocket协议以及ws源码分析

为什么使用 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.toPrimitivevalueOf 方法

栗子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 数组

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 , 但进行了裁剪, 注意两个对象所分配的内存是重叠的, 修改一个会影响另一个

  • base64string 的转换

// Y2FsYWJhc2g=
const base64Str = Buffer.from('calabash').toString('base64')
// calabash
Buffer.from(base64Str, 'base64').toString()
复制代码

6. END

到这里基本完成 Buffer API 的学习, 不过以我目前的水平, 没有需要自己封包解包的场景, 只有看别人封包解包了TAT


以上就是本文的全部内容,希望对大家的学习有所帮助,也希望大家多多支持 码农网

查看所有标签

猜你喜欢:

本站部分资源来源于网络,本站转载出于传递更多信息之目的,版权归原作者或者来源机构所有,如转载稿涉及版权问题,请联系我们

产品经理的20堂必修课

产品经理的20堂必修课

徐建极 / 人民邮电出版社 / 2013-9-1 / 59.00元

《产品经理的20堂必修课》以作者八年的产品经理工作实践为基础,通过系统的理论结合丰富的实例的方法,全面地总结了作为一名互联网产品经理所应掌握的知识。 《产品经理的20堂必修课》分为三大部分。 讲产品:深入剖析互联网产品成功的要素,分别从需求导向、简单原则、产品运营、战略布局等维度,分析如何让产品在残酷的互联网竞争中脱颖而出。 讲方法:着重分析优秀的产品团队运作的工作方法和程序,详......一起来看看 《产品经理的20堂必修课》 这本书的介绍吧!

JSON 在线解析
JSON 在线解析

在线 JSON 格式化工具

随机密码生成器
随机密码生成器

多种字符组合密码

正则表达式在线测试
正则表达式在线测试

正则表达式在线测试