Express全系列教程之(十六):认识Buffer

    xiaoxiao2024-11-04  65

    一、简介

    在nodejs中,我们无法直接发送二进制数据,如图像、视频等媒体文件。而Buffer的出现就是专门用来存储二进制数据的。Buffer是node.js的核心模块,因此它可以直接通过Buffer.from();来将数据存储进被分配的内存中。

    Buffer.from(str);

    我们写入的虽然是字符串,但当我们打印后得到的确实16进制数据,也因为如此,一个Buffer所对应的取值范围为00-FF,即0-255之间,而对应的二进制则是:00000000-11111111(一位代表四位二进制数)。 在这里我们可以了解一下数据存储规则:

    一位二进制=1bit 8bit=1byte(一字节) 1kb=1024byte

    在这里,因为字母汉字在所占内存容量也有所不同,如下:

    var str="Hello 李" var buf=Buffer.from(str,"UTF-8"); console.log(str.length); // 7 console.log(buf.length); // 9

    我们可以看到,这里一个字是占3个字节的。

    二、创建Buffer 类

    1.可以通过 Buffer.alloc(size);来创建一个固定长度的Buffer类,且空间是连续的; Nodejs并不会去操作内存,它把这一工作交给C++完成内存分配申请,再通过JavaScript进行管理。

    var buf=Buffer.alloc(5); //分配长度为5的连续内存空间,他的初始值为0

    我们也可以为创建的内存空间赋值:

    buf[0]=39; //39 buf[1]=-100; //156 buf[2]=257; //256 buf[3]=0xaa //170 buf[5]=1 //打印buf时,值不会变化

    值得注意的是Buffer一旦创建,它的值就不会改变。想在创建时分配器初始值,则可写为: Buffer.alloc(10, 1); 它每一项的初始值都会为01

    2.通过Buffer.allocUnsafe(10);来创建固定长度Buffer,通过allocUnsafe创建的内存空间将不会初始化数据,返回的 Buffer 实例可能包含旧数据。

    四、缓冲区操作

    1.写入缓冲区
    buf.write(string[, offset[, length]][, encoding]); string - 写入缓冲区的字符串。 offset - 缓冲区开始写入的索引值,默认为 0 。 length - 写入的字节数,默认为 buffer.length encoding - 使用的编码。默认为 'utf8' 。

    如果Buffer分配空间不足,则只会写入一部分:

    buf = Buffer.alloc(10); len = buf.write("www.bilibili.com"); console.log("字节数 : "+ len); //字节数10
    2.读取缓冲区数据
    buf.toString([encoding[, start[, end]]]); encoding - 使用的编码。默认为 'utf8' 。 start - 指定开始读取的索引位置,默认为 0。 end - 结束位置,默认为缓冲区的末尾。

    实例:

    buf = Buffer.alloc(26); for (var i = 0 ; i < 26 ; i++) { buf[i] = i + 97; } console.log( buf.toString('ascii')); // 输出: abcdefghijklmnopqrstuvwxyz console.log( buf.toString('ascii',0,5)); // 输出: abcde console.log( buf.toString('utf8',0,5)); // 输出: abcde console.log( buf.toString(undefined,0,5)); // 使用 'utf8' 编码, 并输出: abcde
    3.合并缓冲区

    通过 Buffer.concat(list[, totalLength]);合并缓冲区:

    var buffer1 = Buffer.from(('张三')); var buffer2 = Buffer.from(('李四')); var buffer3 = Buffer.concat([buffer1,buffer2]); console.log("buffer3: " + buffer3.toString()); //buffer3:张三李四
    4.缓冲区前后比较

    buf.compare(otherBuffer);可以对缓冲区进行比较:

    var buffer1 = Buffer.from('ABC'); var buffer2 = Buffer.from('ABCD'); var result = buffer1.compare(buffer2); if(result < 0) { console.log(buffer1 + " 在 " + buffer2 + "之前"); //ABC在ABCD之前 }else if(result == 0){ console.log(buffer1 + " 与 " + buffer2 + "相同"); }else { console.log(buffer1 + " 在 " + buffer2 + "之后"); }
    5.拷贝缓冲区
    buf.copy(targetBuffer[, targetStart[, sourceStart[, sourceEnd]]]); targetBuffer - 要拷贝的 Buffer 对象。 targetStart - 拷贝到目标第几位上,默认0 sourceStart - 从第几位开始拷贝, 默认: 0 sourceEnd - 拷贝结束位,默认: buffer.length

    如下实例:

    var buf1 = Buffer.from('abcdefgh'); var buf2 = Buffer.from('ABC'); buf2.copy(buf1, 2,0); console.log(buf1.toString()); //abABCfgh
    6.缓冲区长度

    buf.length;即可获得缓冲区长度

    var buffer = Buffer.from('www.runoob.com'); // 缓冲区长度 console.log("buffer length: " + buffer.length); //buffer length: 14

    五、关于内存分配

    之前js只能处理字符串数据类型,且对字符串的操作都是得到一个新的字符串,不会对源字符串做出改变,而buffer会通过buffer[index]的方式对固定位置的字节进行更改。Buffer是一个典型的Javascript和C++结合的模块,性能相关部分用C++实现,非性能相关部分用javascript实现。Node在进程启动时Buffer就已经加装进入内存,并将其放入全局对象,因此无需require。 Buffer对象的内存分配不是在V8的堆内存中,在Node的C++层面实现内存的申请。 Nodejs采用slab分配机制,slab是一种动态内存管理机制,slab有三种状态:

    full:完全分配状态 partial:部分分配状态 empty:没有被分配状态

    nodejs会根据分配内存大小对Buffer对象进行分类,申请内存小于4kb时,就会存入初始化的slab单元中。当继续申请小于4kb且当前第一个初始化的8k空间足够的情况下会继续存入第一个初始化的8kkong空间池(如果被初始化的8k池的空间剩余2k,这个时候再去申请一个大于2k并且小于4k的内存空间,就会去新申请一个slab单元空间,上次初始化的slab单元的剩余2k内存就会被浪费掉,无法再使用)。 Buffer的分配过程中主要使用一个局部变量pool作为中间处理对象,处于分配状态的slab单元都指向它。

    var pool; function allocPool() { pool = new SlowBuffer(Buffer.poolSize); pool.used = 0; }

    如果pool没有被创建,将会创建一个新的slab单元指向它:

    if (!pool || pool.length - pool.used < this.length) allocPool();

    同时当前Buffer对象的parent属性指向该slab,并记录下是从这个slab的哪个位置(offset)开始使用的,slab对象自身也记录被使用了多少字节:

    this.parent = pool; this.offset = pool.used; pool.used += this.length; if (pool.used & 7) pool.used = (pool.used + 8) & ~7;

    这时候的slab状态为partial。当再次创建一个Buffer对象时,构造过程中将会判断这个slab的剩余空间是否足够。如果足够,使用剩余空间。如果slab剩余的空间不够,将会构造新的slab,原slab中剩余的空间会造成浪费。

    最新回复(0)