이 기사는 Node의 Buffer 클래스에 대한 심층적인 이해를 제공합니다.

青灯夜游
풀어 주다: 2022-12-12 20:20:57
앞으로
1862명이 탐색했습니다.

이 기사는 Node의 Buffer 클래스에 대한 심층적인 이해를 제공할 것입니다.

이 기사는 Node의 Buffer 클래스에 대한 심층적인 이해를 제공합니다.

TypedArray가 나오기 전에는 JavaScript 언어가 raw Binary Data(원시 바이너리 데이터)를 잘 처리하지 못했습니다. 이는 초기에는 브라우저에서 Scripting 언어를 사용하면서 주로 JavaScript를 사용했기 때문입니다. 이므로 기본 바이너리 데이터를 처리해야 하는 시나리오는 거의 없습니다. Node가 나온 이후 서버측 애플리케이션은 파일 읽기 및 쓰기, TCP 연결 등 수많은 바이너리 스트림을 처리해야 하기 때문에 Node에서는 JavaScript 외에 새로운 데이터 유형 Buffer를 정의했습니다. (V8) . Buffer는 Node 애플리케이션에서 널리 사용되므로 사용법을 완전히 숙지해야만 더 나은 Node 애플리케이션을 작성할 수 있습니다. [관련 튜토리얼 추천 : nodejs 동영상 튜토리얼, 프로그래밍 교육]

바이너리 기초


Buffer의 구체적인 사용법을 정식으로 소개하기 전에 바이너리에 대한 지식을 간단히 복습해 보겠습니다.

프로그래머로서 우리 모두는 바이너리에 익숙해야 합니다. 왜냐하면 컴퓨터의 모든 기본 데이터는 바이너리 형식으로 저장되기 때문입니다. 즉, 일반 텍스트든 사진이든 동영상이든 컴퓨터에 있는 파일은 컴퓨터 하드 드라이브에 있는 두 개의 숫자 01로 구성됩니다. 컴퓨터 과학에서는 단일 숫자 0 또는 1bit(비트)라고 부르며, 8 bitsbyte(바이트)를 형성할 수 있습니다. 십진수 16을 1바이트로 표현하면 기본 저장 구조는 다음과 같습니다. 截屏2022-10-15 下午2.23.13.png16을 이진수로 표현하면 십진수 표현에 비해 6자리가 더 많다는 것을 알 수 있습니다. 이진수가 더 크면 읽고 쓰는 것이 매우 불편할 것입니다. 이런 이유로 프로그래머들은 일반적으로 데이터를 표현하기 위해 바이너리를 직접 사용하는 대신 color 값을 16진수(예: #FFFFFF)로 표현하는 것을 선호합니다. 0과 1의. 문자 인코딩

모든 데이터의 최하층은 바이너리이고, 네트워크를 통해 전송되는 데이터도 바이너리인데 왜 지금 우리가 읽고 있는 기사는 0 묶음이 아닌

중국어

로 되어있나요? 그리고 1? 여기서는 문자 인코딩의 개념을 소개하겠습니다. 소위 문자 인코딩은 문자(한자, 영어 문자 또는 기타 문자)가 이진수(여러 바이트 포함)에 어떻게 대응하는지를 나타내는 매핑 관계 테이블입니다. 예를 들어, 익숙한 ascii를 사용하여 인코딩하는 경우 영어 문자 a의 이진 표현은 0b01100001입니다(0b는 이진수의 접두어입니다). 따라서 우리 컴퓨터가 ascii로 인코딩된 파일 에서 이진 데이터의 0b01100001 문자열을 읽으면 a 문자가 화면에 표시되고 a 문자도 컴퓨터에 저장됩니다. 네트워크에서 전송되는 바이너리 데이터는 0b01100001입니다. ascii 코드 외에도 일반적인 문자 인코딩에는 utf-8utf-16 등이 포함됩니다. Buffer

기본적인 바이너리 지식


문자 인코딩

의 개념을 익히고 나면 마침내 Buffer을 정식으로 배울 수 있습니다. Buffer의 공식 정의를 살펴보겠습니다:
인스턴스에서는 16진수 값으로 된 값 체인을 얻게 됩니다.

간단히 말하면 소위 BufferV8 힙 메모리 외부에서 노드가 할당한 고정 크기 메모리 공간입니다. console.log를 사용하여 Buffer를 출력하면 16진수로 표현된 값의 문자열이 bytes 단위로 출력됩니다.

버퍼 생성

Buffer의 기본 개념을 이해한 후, Buffer 객체를 생성해 보겠습니다. Buffer를 생성하는 방법에는 여러 가지가 있으며, 일반적인 방법은 Buffer.alloc, Buffer.allocUnsafeBuffer.from입니다.

Buffer.alloc(size[, fill[, 인코딩]])

버퍼를 생성하는 가장 일반적인 방법입니다. Buffer의 크기만 전달하면 됩니다.

const buff = Buffer.alloc(5)

console.log(buff)
// Prints: <Buffer 00 00 00 00 00>
로그인 후 복사

위 코드에서는 5바이트버퍼 영역에서 console.log 함수는 현재 버퍼에 저장된 콘텐츠를 나타내는 5개의 연속된 16진수 숫자를 인쇄합니다. 현재 버퍼가 Node의 기본 동작인 0으로 채워져 있는 것을 볼 수 있습니다. 다음 두 매개변수 fillencoding을 설정하여 초기화 중에 채워질 추가 콘텐츠를 지정할 수 있습니다.

여기서는 node:buffer 패키지에서 명시적으로 가져오지 않고 위 코드에서 Node의 전역 Buffer 개체를 사용했다는 점을 언급할 가치가 있습니다. 이는 전적으로 작성의 편의성과 실제 개발에서 나중에해야 하기 때문입니다.

import { Buffer } from &#39;node:buffer&#39;
로그인 후 복사

Buffer.allocUnsafe(size)

Buffer.allocUnsafe Buffer.alloc의 가장 큰 차이점은 allocUnsafe 함수 사용에 적용되는 메모리 공간이 none이라는 점입니다. 초기화는 지난번에 사용했던 데이터가 아직 남아있을 수 있다는 뜻이므로 데이터 보안 문제가 발생할 수 있습니다. allocUnsafe 함수는 버퍼 영역의 크기로 size 매개변수를 받습니다.

const buff = Buffer.allocUnsafe(5)

console.log(buff)
// Prints (实际内容可能有出入): <Buffer 8b 3f 01 00 00>
로그인 후 복사

위의 출력 결과로 판단하면 Buffer.allocUnsafe를 사용하여 할당된 버퍼 내용을 제어할 수 없습니다. 이 함수가 Buffer.alloc보다 더 빠르게 Buffer를 할당하는 것은 할당된 메모리가 초기화되지 않기 때문입니다. 실제 개발에서는 실제 요구 사항에 따라 선택해야 합니다.

Buffer.from

이 함수는 버퍼를 생성하는 데 가장 일반적으로 사용되는 함수입니다. 이 함수에는 다양한 오버로드가 있으므로 다른 매개변수를 전달하면 동작도 달라집니다. 몇 가지 일반적인 오버로드를 살펴보겠습니다.

Buffer.from(string[, 인코딩])

우리가 전달하는 첫 번째 매개 변수가 string 유형인 경우 Buffer.from은 문자열을 기반으로 합니다. 인코딩(encoding 매개변수, 기본값은 utf8)은 문자열에 해당하는 이진 표현을 생성합니다. 예를 들어보세요.

const buff = Buffer.from(&#39;你好世界&#39;)

console.log(buff)
// Prints: <Buffer e4 bd a0 e5 a5 bd e4 b8 96 e7 95 8c>
console.log(buff.toString())
// Prints: &#39;你好世界&#39;
console.log(buff.toString(&#39;ascii&#39;))
// Prints: &#39;&#39;d= e%=d8\x16g\x15\f&#39;&#39;
로그인 후 복사

위의 예에서는 "Hello World" 문자열을 사용하여 버퍼 초기화를 완료했습니다. 두 번째 encoding 매개변수를 전달하지 않았으므로 기본값은 utf8 코딩입니다. 나중에 첫 번째 console.log의 출력을 보면 우리가 전달한 문자열에는 4자만 있지만 초기화된 버퍼에는 12바이트가 있다는 것을 알 수 있습니다. 이는 utf8이 인코딩되기 때문입니다. 한자는 3바이트로 표현됩니다. 그런 다음 buff.toString() 메서드를 사용하여 버프의 내용을 확인합니다. toString 메서드의 기본 인코딩 출력 형식은 utf8이므로 두 번째 console.log에서 버프 내용을 올바르게 출력할 수 있음을 알 수 있습니다. 버프에 저장된 콘텐츠입니다. 그러나 세 번째 console.log에서는 문자 인코딩 유형을 ascii로 지정합니다. 이때 잘못된 문자가 많이 표시됩니다. 이걸 보시면 아까 말씀드렸던 문자인코딩에 대한 이해가 더 깊어지셨을 거라 생각합니다.

Buffer.from(buffer)

Buffer.from이 받은 매개변수가 버퍼 객체인 경우 Node는 새 Buffer 인스턴스를 생성한 다음 전달된 버퍼 내용을 새 Buffer 객체에 복사합니다.

const buf1 = Buffer.from(&#39;buffer&#39;)
const buf2 = Buffer.from(buf1)

console.log(buf1)
// Prints: <Buffer 62 75 66 66 65 72>
console.log(buf2)
// Prints: <Buffer 62 75 66 66 65 72>

buf1[0] = 0x61

console.log(buf1.toString())
// Prints: auffer
console.log(buf2.toString())
// Prints: buffer
로그인 후 복사

위의 예에서는 먼저 Buffer 객체

buf1를 생성했고, 여기에 저장된 내용은 "buffer"라는 문자열이며, 그런 다음 이 Buffer 객체를 통해 새 Buffer 객체 buf2를 초기화했습니다. 이때 buf1의 첫 번째 바이트를 (a의 인코딩)로 변경했는데, buf1의 출력은 0x61auffer가 된 반면 buf2의 내용은 변경되지 않아 Buffer임을 확인했습니다. from(buffer)는 데이터 복사 뷰입니다.

?注意:当Buffer的数据很大的时候,Buffer.from拷贝数据的性能是很差的,会造成CPU占用飙升,主线程卡死的情况,所以在使用这个函数的时候一定要清楚地知道Buffer.from(buffer)背后都做了什么。笔者就在实际项目开发中踩过这个坑,导致线上服务响应缓慢!

Buffer.from(arrayBuffer[, byteOffset[, length]])

说完了buffer参数,我们再来说一下arrayBuffer参数,它的表现和buffer是有很大的区别的。ArrayBuffer是ECMAScript定义的一种数据类型,它简单来说就是一片你不可以直接(或者不方便)使用的内存,你必须通过一些诸如Uint16ArrayTypedArray对象作为View来使用这片内存,例如一个Uint16Array对象的.buffer属性就是一个ArrayBuffer对象。当Buffer.from函数接收一个ArrayBuffer作为参数时,Node会创建一个新的Buffer对象,不过这个Buffer对象指向的内容还是原来ArrayBuffer的内容,没有任何的数据拷贝行为。我们来看个例子:

const arr = new Uint16Array(2)

arr[0] = 5000
arr[1] = 4000

const buf = Buffer.from(arr.buffer)

console.log(buf)
// Prints: <Buffer 88 13 a0 0f>

// 改变原来数组的数字
arr[1] = 6000

console.log(buf)
// Prints: <Buffer 88 13 70 17>
로그인 후 복사

从上面例子的输出我们可以知道,arrbuf对象会共用同一片内存空间,所以当我们改变原数组的数据时,buf的数据也会发生相应的变化。

其它Buffer操作

看完了创建Buffer的几种做法,我们接着来看一下Buffer其它的一些常用API或者属性

buf.length

这个函数会返回当前buffer占用了多少字节

// 创建一个大小为1234字节的Buffer对象
const buf1 = Buffer.alloc(1234)
console.log(buf1.length)
// Prints: 1234

const buf2 = Buffer.from(&#39;Hello&#39;)
console.log(buf2.length)
// Prints: 5
로그인 후 복사

Buffer.poolSize

这个字段表示Node会为我们预创建的Buffer池子有多大,它的默认值是8192,也就是8KB。Node在启动的时候,它会为我们预创建一个8KB大小的内存池,当用户用某些API(例如Buffer.alloc)创建Buffer实例的时候可能会用到这个预创建的内存池以提高效率,下面是一个具体的例子:

const buf1 = Buffer.from(&#39;Hello&#39;)
console.log(buf1.length)
// Prints: 5

// buf1的buffer属性会指向其底层的ArrayBuffer对象对应的内存
console.log(buf1.buffer.byteLength)
// Prints: 8192

const buf2 = Buffer.from(&#39;World&#39;)
console.log(buf2.length)
// Prints: 5

// buf2的buffer属性会指向其底层的ArrayBuffer对象对应的内存
console.log(buf2.buffer.byteLength)
// Prints: 8192
로그인 후 복사

在上面的例子中,buf1buf2对象由于长度都比较小所以会直接使用预创建的8KB内存池。其在内存的大概表示如图:截屏2022-12-11 下午1.51.54.png这里值得一提的是只有当需要分配的内存区域小于4KB(8KB的一半)并且现有的Buffer池子还够用的时候,新建的Buffer才会直接使用当前的池子,否则Node会新建一个新的8KB的池子或者直接在内存里面分配一个区域(FastBuffer)。

buf.write(string[, offset,[, length]][, encoding])

这个函数可以按照一定的偏移量(offset)往一个Buffer实例里面写入一定长度(length)的数据。我们来看一下具体的例子:

const buf = Buffer.from(&#39;Hello&#39;)

console.log(buf.toString())
// Prints: "Hello"

// 从第3个位置开始写入&#39;LLO&#39;字符
buf.write(&#39;LLO&#39;, 2)
console.log("HeLLO")
// Prints: "HeLLO"
로그인 후 복사

这里需要注意的是当我们需要写入的字符串的长度超过buffer所能容纳的最长字符长度(buf.length)时,超过长度的字符会被丢弃:

const buf = Buffer.from(&#39;Hello&#39;)

buf.write(&#39;LLO!&#39;, 2)
console.log(buf.toString())
// Print:s "HeLLO"
로그인 후 복사

另外,当我们写入的字符长度超过buffer的最长长度,并且最后一个可以写入的字符不能全部填满时,最后一个字符整个不写入:

const buf = Buffer.from(&#39;Hello&#39;)

buf.write(&#39;LL你&#39;, 2)
console.log(buf.toString())
// Prints "HeLLo"
로그인 후 복사

在上面的例子中,由于"你"是中文字符,需要占用三个字节,所以不能全部塞进buf里面,因此整个字符的三个字节都被丢弃了,buf对象的最后一个字节还是保持"o"不变。

Buffer.concat(list[, totalLength])

这个函数可以用来拼接多个Buffer对象生成一个新的buffer。函数的第一个参数是待拼接的Buffer数组,第二个参数表示拼接完的buffer的长度是多少(totalLength)。下面是一个简单的例子:

const buf1 = Buffer.from(&#39;Hello&#39;)
const buf2 = Buffer.from(&#39;World&#39;)

const buf = Buffer.concat([buf1, buf2])
console.log(buf.toString())
// Prints "HelloWorld"
로그인 후 복사

上面的例子中,因为我们没有指定最终生成Buffer对象的长度,所以Node会计算出一个默认值,那就是buf.totalLength = buf1.length + buf2.length。而如果我们指定了totalLength的值的话,当这个值比buf1.lengh + buf2.length小时,Node会截断最后生成的buffer;如果指定的值比buf1.length + buf2.length大时,生成buf对象的长度还是totalLength,多出来的位数填充的内容是0。

这里还有一点值得指出的是,Buffer.concat最后拼接出来的Buffer对象是通过拷贝原来Buffer对象得出来,所以改变原来的Buffer对象的内容不会影响到生成的Buffer对象,不过这里我们还是需要考虑拷贝的性能问题就是了。

버퍼 개체의 가비지 컬렉션

글 시작 부분에서 Node의 모든 버퍼 개체에 의해 할당된 메모리 영역은 V8힙 공간과 독립적이며 오프 힙 메모리에 속한다고 말했습니다. 그렇다면 이는 Buffer 객체가 V8 가비지 수집 메커니즘의 영향을 받지 않고 수동으로 메모리를 관리해야 한다는 의미인가요? 실제로는 아닙니다. Node의 API를 사용하여 새 Buffer 객체를 생성할 때마다 각 Buffer 객체는 JavaScript 공간의 객체(버퍼 메모리에 대한 참조)에 해당합니다. 이 객체는 V8 가비지 수집에 의해서만 제어됩니다. 이 참조가 가비지 수집될 때 버퍼가 가리키는 오프 힙 메모리를 해제하기 위해 몇 가지 후크를 걸어 놓습니다. 간단히 말하면, Buffer가 할당한 공간에 대해 걱정할 필요가 없습니다. V8의 가비지 수집 메커니즘은 쓸모 없는 메모리를 회수하는 데 도움이 됩니다.

요약

이 기사에서는 일반적인 Buffer API 및 속성을 포함하여 Buffer에 대한 몇 가지 기본 지식을 소개했습니다. 이 지식이 작업에 도움이 되기를 바랍니다.

노드 관련 지식을 더 보려면 nodejs 튜토리얼을 방문하세요!

위 내용은 이 기사는 Node의 Buffer 클래스에 대한 심층적인 이해를 제공합니다.의 상세 내용입니다. 자세한 내용은 PHP 중국어 웹사이트의 기타 관련 기사를 참조하세요!

관련 라벨:
원천:juejin.cn
본 웹사이트의 성명
본 글의 내용은 네티즌들의 자발적인 기여로 작성되었으며, 저작권은 원저작자에게 있습니다. 본 사이트는 이에 상응하는 법적 책임을 지지 않습니다. 표절이나 침해가 의심되는 콘텐츠를 발견한 경우 admin@php.cn으로 문의하세요.
인기 튜토리얼
더>
최신 다운로드
더>
웹 효과
웹사이트 소스 코드
웹사이트 자료
프론트엔드 템플릿