Node.js - Buffer模塊API整理

?

最近的幾篇文章中都涉及到了Buffer, 雖然用過幾個API, 仍是系統總結一下, 主要針對API, 具體使用會少一點web

Remote Procedure Calljson

WebSocket協議以及ws源碼分析數組

爲何使用Buffer?bash

在引入 TypedArray 以前,JavaScript 語言沒有用於讀取或操做二進制數據流的機制。 Buffer 類是做爲 Node.js API 的一部分引入的,用於在 TCP 流、文件系統操做、以及其餘上下文中與八位字節流進行交互。網絡

最經常使用的場景在於:函數

  • TCP流: 目前遇到用在封包和解包階段, 提到的三篇文章中都有涉及源碼分析

    • WebSocket數據幀
    • RPC通信協議
    • 等等等等
  • 文件系統操做學習

參考ui

網絡序?本地序?傻傻分不清楚。。。spa

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

相關文章
相關標籤/搜索