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


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

查看所有标签

猜你喜欢:

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

微信民族志、自媒体时代的知识生产与文化实践

微信民族志、自媒体时代的知识生产与文化实践

赵旭东 / 中国社会科学出版社 / 2017-9 / 98.00元

进入二十一世纪以来,随着网络技术的发展,自媒体的悄然登场深度影响着我们的日常生活。中国社会中自媒体通讯方式的普及以及随之而有的一种文化书写的新形式——微信民族志的出现使原有文化秩序中时空意义发生转变的同时,也在重新塑造着以研究异文化为己任的人类学学科自身的成长、转型与发展。在此种情境之下,由中国人民大学人类学研究所、中国人民大学国家发展与战略研究院、中国人民大学社会学理论与方法研究中心、《探索与争......一起来看看 《微信民族志、自媒体时代的知识生产与文化实践》 这本书的介绍吧!

CSS 压缩/解压工具
CSS 压缩/解压工具

在线压缩/解压 CSS 代码

HTML 编码/解码
HTML 编码/解码

HTML 编码/解码

UNIX 时间戳转换
UNIX 时间戳转换

UNIX 时间戳转换