资讯专栏INFORMATION COLUMN

Node Buffer解读

Zack / 780人阅读

摘要:是什么存在于全局对象上,无需引入模块即可使用,可见重要性非同一般。可以理解是在内存中开辟的一片区域,用于存放二进制数据。大小通过参数指定,默认情况下是。一般情况下位系统大约是,位系统大约是。

Buffer是什么?

Buffer存在于全局对象上,无需引入模块即可使用,可见重要性非同一般。
可以理解Buffer是在内存中开辟的一片区域,用于存放二进制数据。Buffer所开辟的是堆外内存。

Buffer的应用场景有哪些?

怎么理解流呢?流是数据的集合(与数据、字符串类似),但是流的数据不能一次性获取到,数据也不会全部load到内存中,因此流非常适合大数据处理以及断断续续返回chunk的外部源。流的生产者与消费者之间的速度通常是不一致的,因此需要buffer来暂存一些数据。buffer大小通过highWaterMark参数指定,默认情况下是16Kb。

存储需要占用大量内存的数据

Buffer 对象占用的内存空间是不计算在 Node.js 进程内存空间限制上的,所以可以用来存储大对象,但是对象的大小还是有限制的。一般情况下32位系统大约是1G,64位系统大约是2G。

如何创建Buffer

除了流自动隐式创建Buffer之外,也可以手动创建Buffer,方式如下:

Buffer中存储的数据已确定

Buffer.from(obj) // obj支持的类型string, buffer, arrayBuffer, array, or array-like object

注意:Buffer.from不支持传入数字,如下所示:

Buffer.from(1234);

buffer.js:208
    throw new errors.TypeError(
    ^

TypeError [ERR_INVALID_ARG_TYPE]: The "value" argument must not be of type number. Received type number
    at Function.from (buffer.js:208:11)
    ...
    

若要传入数字可以采用传入数组的方式:

const buf = Buffer.from([1, 2, 3, 4]);
console.log(buf); // 

但是这种方式存在一个问题,当存入不同的数值的时候buffer中记录的二进制数据会相同,如下所示:

const buf2 = Buffer.from([127, -1]);
console.log(buf2);    

const buf3 = Buffer.from([127, 255]);
console.log(buf3);    

console.log(buf3.equals(buf2));  // true

当要记录的一组数全部落在0到255(readUInt8来读取)这个范围, 或者全部落在-128到127(readInt8来读取)这个范围那么就没有问题,否则的话就强烈不推荐使用Buffer.from来保存一组数。因为不同的数字读取时应该调用不同的方法。

Buffer存储数据未确定

Buffer.alloc、Buffer.allocUnsafe、Buffer.allocUnsafeSlow

Buffer.alloc会用0值填充已分配的内存,所以相比后两者速度上要慢,但是也较为安全。当然也可以通过--zero-fill-buffers flag使allocUnsafe、allocUnsafeSlow在分配完内存后也进行0值填充。

node --zero-fill-buffers index.js

当分配的空间小于4KB的时候,allocUnsafe会直接从之前预分配的Buffer里面slice空间,因此速度比allocUnsafeSlow要快,当大于等于4KB的时候二者速度相差无异。

// 分配空间等于
function createBuffer(fn, size) {
  console.time("buf-" + fn);
  for (var i = 0; i < 100000; i++) {
    Buffer[fn](size);
  }
  console.timeEnd("buf-" + fn);
}
createBuffer("alloc", 4096);
createBuffer("allocUnsafe", 4096);
createBuffer("allocUnsafeSlow", 4096);

// 输出
buf-alloc:           294.002ms
buf-allocUnsafe:     224.072ms
buf-allocUnsafeSlow: 209.22ms
function createBuffer(fn, size) {
  console.time("buf-" + fn);
  for (var i = 0; i < 100000; i++) {
    Buffer[fn](size);
  }
  console.timeEnd("buf-" + fn);
}
createBuffer("alloc", 4095);
createBuffer("allocUnsafe", 4095);
createBuffer("allocUnsafeSlow", 4095);
// 输出
buf-alloc:           296.965ms
buf-allocUnsafe:     135.877ms
buf-allocUnsafeSlow: 205.225ms

需要谨记一点:new Buffer(xxxx) 方式已经不推荐使用了

Buffer使用
buffer转字符串
const buf = Buffer.from("test");
console.log(buf.toString("utf8"));                 // test
console.log(buf.toString("utf8", 0, 2));           // te
buffer转json
const buf = Buffer.from([0x1, 0x2, 0x3, 0x4, 0x5]);
console.log(buf.toJSON());    // { type: "Buffer", data: [ 1, 2, 3, 4, 5 ] }
buffer裁剪,裁剪后返回的新的buffer与原buffer指向同一块内存
buf.slice([start[, end]])
start 起始位置
end 结束位置(不包含)

示例:

var buf1 = Buffer.from("test");
var buf2 = buf1.slice(1, 3).fill("xx");
console.log("buf2 content: " + buf2.toString()); // xx
console.log("buf1 content: " + buf1.toString()); // txxt
buffer拷贝,buffer与数组不同,buffer的长度一旦确定就不再变化,因此当拷贝的源buffer比目标buffer大时只会复制部分的值
buf.copy(target[, targetStart[, sourceStart[, sourceEnd]]])

示例:
var buf1 = Buffer.from("abcdefghijkl");
var buf2 = Buffer.from("ABCDEF");

buf1.copy(buf2, 1);
console.log(buf2.toString()); //Abcdef
buffer相等判断,比较的是二进制值
buf.equals(otherBuffer)

示例:
const buf1 = Buffer.from("ABC");
const buf2 = Buffer.from("414243", "hex"); 
console.log(buf1.equals(buf2));    // true

除了equals之外,compare其实也可以用于判断是否相等(当结果为0则相等),不过compare更主要的作用是用于对数组内的buffer实例排序。

buffer是否包含特定值
buf.includes(value[, byteOffset][, encoding])
buf.indexOf(value[, byteOffset][, encoding])

示例:
const buf = Buffer.from("this is a buffer");
console.log(buf.includes("this"));  // true
console.log(buf.indexOf("this"));  // 0
写入读取数值

写入方法:

write{Double| Float | Int16 | Int32| UInt16 | UInt32 }{BE|LE}(value, offset)

write{Int | UInt}{BE | LE}(value, offset, bytelength) // 此方法提供了更灵活的位数表示数据(比如3位、5位)

write{Int8 | Unit8}(value, offset)

读取方法:

write{Double| Float | Int16 | Int32 | UInt16 | UInt32 }{BE|LE}(offset)

read{Int | UInt}{BE | LE}(offset, byteLength)

read{Int8 | Unit8}(offset)

Double、Float、Int16、Int32、UInt16、UInt32既确定了表征数字的位数,也确定了是否包含负数,因此定义了不同的数据范围。同时由于表征数字的位数都超过8位,无法用一个字节来表示,因此就涉及到了计算机的字节序区分(大端字节序与小端字节序)

关于大端小端的区别可以这么理解:数值的高位在buffer的起始位置的是大端,数值的低位buffer的起始位置则是小端

const buf = Buffer.allocUnsafe(2);
buf.writeInt16BE(256, 0)  
console.log(buf);           //  
buf.writeInt16LE(256, 0)
console.log(buf);           // 

https://tool.lu/hexconvert/ 这里可以查看数值的不同进制之间的转换,如果是大端的话,则直接按顺序(0100)拼接16进制即可,如果是小端则需要调换一下顺序才是正确的表示方式。

buffer合并
Buffer.concat(list[, totalLength]) //totalLength不是必须的,如果不提供的话会为了计算totalLength会多一次遍历

const buf1 = Buffer.from("this is");
const buf2 = Buffer.from(" funny");
console.log(Buffer.concat([buf1, buf2], buf1.length + buf2.length));
// 
清空buffer

清空buffer数据最快的办法是buffer.fill(0)

buffer模块与Buffer的关系

Buffer是全局global上的一个引用,指向的其实是buffer.Buffer

 const buffer = require("buffer");
 console.log(buffer.Buffer === Buffer); //true

buffer模块上还有其他一些属性和方法

const buffer = require("buffer");
console.log(buffer);
{ Buffer:
   { [Function: Buffer]
     poolSize: 8192,
     from: [Function: from],
     alloc: [Function: alloc],
     allocUnsafe: [Function: allocUnsafe],
     allocUnsafeSlow: [Function: allocUnsafeSlow],
     isBuffer: [Function: isBuffer],
     compare: [Function: compare],
     isEncoding: [Function: isEncoding],
     concat: [Function: concat],
     byteLength: [Function: byteLength],
     [Symbol(node.isEncoding)]: [Function: isEncoding] },
  SlowBuffer: [Function: SlowBuffer],
  transcode: [Function: transcode],
  INSPECT_MAX_BYTES: 50,
  kMaxLength: 2147483647,
  kStringMaxLength: 1073741799,
  constants: { MAX_LENGTH: 2147483647, MAX_STRING_LENGTH: 1073741799 } }

上面的kMaxLength与MAX_LENGTH代表了新建buffer时内存大小的最大值,当超过限制值后就会报错

32为机器上是(2^30)-1(~1GB)

64位机器上是(2^31)-1(~2GB)

Buffer释放

我们无法手动对buffer实例进行GC,只能依靠V8来进行,我们唯一能做的就是解除对buffer实例的引用

参考资料

http://cenalulu.github.io/lin...

http://www.ruanyifeng.com/blo...

https://medium.freecodecamp.o...

https://www.barretlee.com/blo...

https://medium.freecodecamp.o...

http://www.runoob.com/nodejs/...

文章版权归作者所有,未经允许请勿转载,若此文章存在违规行为,您可以联系管理员删除。

转载请注明本文地址:https://www.ucloud.cn/yun/95080.html

相关文章

  • Node.js process模块解读

    摘要:默认情况下,会打印堆栈信息到然后退出进程。适用于父子进程之间发送消息,关于如何创建父子进程会放在模块中进行。信号虽然也是用于请求终止进程,但是它与有所不同,进程可以选择响应还是忽略此信号。 process存在于全局对象上,不需要使用require()加载即可使用,process模块主要做两方面的事情 读:获取进程信息(资源使用、运行环境、运行状态) 写:执行进程操作(监听事件、调度任...

    Riddler 评论0 收藏0
  • Node.js - 200 多行代码实现 Websocket 协议

    摘要:预备工作序最近正在研究相关的知识,想着如何能自己实现协议。监听事件就是协议的抽象,直接在上面监听已有的事件和事件这两个事件。表示当前数据帧为消息的最后一个数据帧,此时接收方已经收到完整的消息,可以对消息进行处理。 A、预备工作 1、序 最近正在研究 Websocket 相关的知识,想着如何能自己实现 Websocket 协议。到网上搜罗了一番资料后用 Node.js 实现该协议,倒也没...

    张巨伟 评论0 收藏0
  • node之stream(上)——readable

    摘要:上游水源通过里中的方法流入水电站中。当在接收数据中出现错误时发出。暂停可读流,不再发出事件恢复可读流,继续发出事件把这个可读流的输出传递给指定的流,两个流组成一个管道。 题外话 该文章整合了多篇网络文章(整合之处已设置超链接,可点击直接了解原文),目的仅仅是为了和大伙分享,更加通俗易懂的了解流的各个流程的初始。本人也是node的初学菜鸟,有描述错误或误人子弟的地方多请大神们多多指出。 ...

    zhangrxiang 评论0 收藏0
  • Node.js 高级进阶之 fs 文件模块学习

    摘要:回调函数提供两个参数和,表示有没有错误发生,是文件内容。文件关闭第一个参数文件时传递的文件描述符第二个参数回调函数回调函数有一个参数错误,关闭文件后执行。 showImg(//img.mukewang.com/5d3f890d0001836113660768.jpg); 人所缺乏的不是才干而是志向,不是成功的能力而是勤劳的意志。 —— 部尔卫 文章同步到github博客:https:/...

    verano 评论0 收藏0
  • nginx http模块配置参数解读

    摘要:名称默认配置作用域官方说明中文解读模块是否开启对后端的缓冲指定一个连接到代理服务器的超时时间,单位为秒,需要注意的是这个时间最好不要超过秒。 序 本文主要解析一下nginx http模块配置参数。主要分socket相关参数,对clinet请求的buffer参数以及对response的buffer参数。 socket 名称 默认配置 作用域 官方说明 中文解读 模块 sendf...

    pf_miles 评论0 收藏0

发表评论

0条评论

Zack

|高级讲师

TA的文章

阅读更多
最新活动
阅读需要支付1元查看
<