Rumah  >  Artikel  >  hujung hadapan web  >  Artikel ini akan memberi anda pemahaman yang mendalam tentang kelas Buffer dalam Node

Artikel ini akan memberi anda pemahaman yang mendalam tentang kelas Buffer dalam Node

青灯夜游
青灯夜游ke hadapan
2022-12-12 19:36:131822semak imbas

Artikel ini akan memberi anda pemahaman yang mendalam tentang kelas Penampan dalam Node saya harap ia akan membantu anda!

Artikel ini akan memberi anda pemahaman yang mendalam tentang kelas Buffer dalam Node

Sebelum TypedArray keluar, bahasa JavaScript tidak dapat mengendalikan data binari mentah(data binari mentah) dengan baik Ya, ini adalah kerana JavaScript digunakan terutamanya sebagai bahasa skrip dalam penyemak imbas pada mulanya, jadi terdapat sangat sedikit senario di mana data binari asli perlu diproses. Selepas Node keluar, kerana aplikasi sebelah pelayan perlu memproses sejumlah besar strim binari seperti membaca dan menulis fail, sambungan TCP, dsb., Node terletak di sebelah JavaScript (V8 Selain itu, jenis data baharu Penimbal ditakrifkan. Memandangkan Buffer digunakan secara meluas dalam aplikasi Nod, hanya dengan benar-benar menguasai penggunaannya anda boleh menulis aplikasi Node yang lebih baik. [Tutorial berkaitan disyorkan: tutorial video nodejs, Pengajaran pengaturcaraan]

Asas Perduaan


Dalam formal pengenalan Sebelum penggunaan khusus Buffer, mari kita semak secara ringkas pengetahuan tentang binari.

Sebagai pengaturcara, kita semua harus biasa dengan binari, kerana semua data asas komputer disimpan dalam format binari. Dalam erti kata lain, fail dalam komputer anda, sama ada ia adalah teks biasa, gambar atau video, terdiri daripada dua nombor 01 dalam pemacu keras komputer. Dalam sains komputer, kami memanggil nombor tunggal 0 atau 1 a bit(bit), dan 8 bit boleh membentuk bait(bait). Jika nombor perpuluhan 16 diwakili oleh 1 bait, struktur storan asas ialah: 截屏2022-10-15 下午2.23.13.png Kita dapat melihat bahawa jika 16 diwakili dalam perduaan, terdapat 6 digit lagi berbanding dengan perwakilan perpuluhan Jika nombor itu lebih besar , digit perduaan akan menjadi lebih banyak, yang akan menjadi sangat menyusahkan kita untuk membaca dan menulis. Atas sebab ini, pengaturcara secara amnya suka menggunakan heksadesimal(heksadesimal) untuk mewakili data dan bukannya menggunakan binari secara langsung Sebagai contoh, apabila kita menulis CSS, kita menggunakan nilai warna Heksadesimal (. cth. #FFFFFF) bukannya sekumpulan 0s dan 1s.

Pengekodan Aksara

Memandangkan lapisan bawah semua data adalah binari dan data yang dihantar melalui rangkaian juga binari, mengapa artikel itu kita sedang membaca sekarangBahasa Cina dan bukannya sekumpulan 0 dan 1? Di sini kami akan memperkenalkan konsep pengekodan aksara . Apa yang dipanggil Pengekodan aksara hanyalah jadual pemetaan, yang mewakili cara aksara (aksara Cina, aksara Inggeris atau aksara lain) dikaitkan dengan nombor binari( mengandungi beberapa bait) yang sepadan antara satu sama lain. Contohnya, jika kita menggunakan ascii biasa untuk mengekod, perwakilan perduaan bagi aksara Inggeris a ialah 0b01100001(0b ialah perduaan awalan nombor). Oleh itu, apabila komputer kita membaca rentetan data binari 0b01100001 daripada fail yang dikodkan dalam ascii , ia akan memaparkan a pada skrin, sama aksara a ialah data binari 0b01100001 apabila disimpan dalam komputer atau dihantar pada rangkaian. Selain kod ascii, pengekodan aksara biasa termasuk utf-8 dan utf-16, dsb.

Buffer


Selepas menguasai asas

pengetahuan binari dan konsep pengekodan aksara, akhirnya kita boleh Secara rasmi mempelajari Buffer. Mari kita lihat definisi rasmi Penimbal:

Kelas

dalam Node.js direka untuk mengendalikan data binari mentah. Setiap penimbal sepadan kepada beberapa memori mentah yang diperuntukkan di luar V8 Penimbal bertindak agak seperti tatasusunan integer, tetapi tidak boleh diubah saiz dan mempunyai sejumlah besar kaedah khusus untuk data binari daripada 0 hingga 255 inklusif Apabila menggunakan Buffer untuk mencetak contoh console.log(), anda akan mendapat rantaian nilai dalam nilai perenambelasan.Buffer

Ringkasnya, apa yang dipanggil Buffer ialah saiz tetap ruang memori yang diperuntukkan oleh Node di luar ingatan timbunan V8. Apabila Buffer dicetak menggunakan console.log, rentetan heksadesimalbait > mewakili nilai.

Buat Penampan

Setelah memahami konsep asas

Penimbal, mari buat Penimbal Objek. Terdapat banyak cara untuk mencipta Buffer, yang biasa ialah Buffer.alloc, Buffer.allocUnsafe dan Buffer.from.

Buffer.alloc(size[, fill[, encoding]])

Ini ialah cara paling biasa untuk mencipta Buffer Anda hanya perlu memasukkan saiz Buffer

const buff = Buffer.alloc(5)

console.log(buff)
// Prints: <Buffer 00 00 00 00 00>
Dalam kod di atas, saya mencipta kawasan Penampan dengan saiz

5 bait Fungsi console.log akan mencetak lima nombor heksadesimal berturut-turut. menunjukkan kandungan yang sedang disimpan dalam Penampan. Kita dapat melihat bahawa Penampan semasa diisi dengan 0, yang merupakan tingkah laku lalai Node Kita boleh menetapkan dua parameter seterusnya isi dan pengekodan untuk menentukan. Isikan kandungan tambahan semasa permulaan.

Perlu dinyatakan di sini bahawa saya menggunakan objek Node global

Buffer dalam kod di atas tanpa mengimportnya secara eksplisit daripada pakej node:buffer , ini sepenuhnya kerana kemudahan menulis. Dalam perkembangan sebenar, yang terakhir harus digunakan:

import { Buffer } from &#39;node:buffer&#39;

Buffer.allocUnsafe(saiz)

Buffer.allocUnsafe

dan Buffer.alloc ialah ruang memori yang digunakan untuk menggunakan fungsi allocUnsafe ialah tidak dimulakan , yang bermaksud bahawa data terakhir yang digunakan mungkin masih kekal, jadi akan terdapat isu keselamatan data. Fungsi alocUnsafe menerima parameter saiz sebagai saiz kawasan penimbal:

Berdasarkan keputusan output di atas, kita tidak boleh mengawal penggunaan
const buff = Buffer.allocUnsafe(5)

console.log(buff)
// Prints (实际内容可能有出入): <Buffer 8b 3f 01 00 00>
Kandungan penimbal yang diperuntukkan oleh allocUnsafe

. Kerana memori yang diperuntukkan tidak dimulakan, fungsi ini memperuntukkan Buffer lebih cepat daripada Buffer.alloc Dalam pembangunan sebenar, kita harus membuat pilihan berdasarkan keperluan sebenar kita.

Buffer.from

Fungsi ini ialah fungsi

kami yang paling biasa digunakan untuk mencipta Penampan Ia mempunyai pelbagai

lebihan, iaitu, menghantar dalam parameter yang berbeza akan mempunyai tingkah laku yang berbeza. Mari lihat beberapa lebihan biasa: Buffer.from(string[, encoding])

Apabila parameter pertama yang kita lalui ialah String

taip, Buffer.from akan menjana perwakilan binari yang sepadan dengan rentetan berdasarkan pengekodan rentetan (

pengekodan parameter, lalai ialah utf8). Ambil contoh: Dalam contoh di atas, saya menggunakan rentetan "Hello World" untuk melengkapkan pemulaan Penampan Memandangkan saya tidak lulus dalam parameter

pengekodan
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;
kedua , jadi pengekodan lalai ialah

utf8. Kemudian, dengan melihat output console.log yang pertama, kita dapati bahawa walaupun rentetan yang kami hantarkan hanya mempunyai empat aksara, Penampan yang dimulakan mempunyai 12 bait kerana aksara Cina dalam pengekodan utf8 akan menggunakan 3 bait untuk mewakilinya. Kemudian kami menggunakan kaedah buff.toString() untuk melihat kandungan buff Memandangkan format output pengekodan lalai kaedah toString ialah utf8, kita boleh melihat console .log boleh mengeluarkan kandungan storan buff dengan betul. Walau bagaimanapun, dalam console.log ketiga, kami menyatakan bahawa jenis pengekodan aksara ialah ascii Pada masa ini, kita akan melihat sekumpulan aksara yang bercelaru. Melihat perkara ini, saya rasa anda mesti mempunyai pemahaman yang lebih mendalam tentang Pengekodan Aksara yang saya nyatakan sebelum ini. Buffer.from(buffer)

Apabila parameter yang diterima oleh Buffer.from ialah objek penampan, Node akan mencipta contoh Buffer baharu dan kemudian menghantarnya dalam The kandungan penimbal disalin ke objek Penampan baharu.

Dalam contoh di atas, kami mula-mula mencipta objek Penampan buf1

, yang menyimpan rentetan "penampan", dan kemudian memulakan objek Penampan Baharu
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
buf2

. Pada masa ini, kami menukar bait pertama buf1 kepada (pengekodan a), dan kami mendapati bahawa output buf1 menjadi auffer dan buf2 tidak berubah, yang mengesahkan pandangan bahawa Buffer.from(buffer) ialah salinan data. 0x61

?注意:当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对象,不过这里我们还是需要考虑拷贝的性能问题就是了。

Pengumpulan sampah objek Penampan

Pada permulaan artikel, saya mengatakan bahawa kawasan memori yang diperuntukkan oleh semua objek Penampan dalam Node adalah bebas daripada V8 Ruang timbunan tergolong dalam ingatan luar timbunan. Jadi adakah ini bermakna objek Penampan tidak terjejas oleh mekanisme kutipan sampah V8 dan kita perlu mengurus memori secara manual? Sebenarnya tidak, setiap kali kami menggunakan API Node untuk mencipta objek Penampan, setiap objek Penampan sepadan dengan objek (rujukan kepada memori Penampan) dalam ruang JavaScript Objek ini dikawal oleh pengumpulan sampah V8 sahaja gantung beberapa cangkuk untuk melepaskan memori luar timbunan yang ditunjuk oleh Penampan apabila rujukan ini ialah sampah yang dikumpul. Ringkasnya, kami tidak perlu risau tentang ruang yang diperuntukkan oleh mekanisme pengumpulan sampah V8 akan membantu kami mendapatkan semula memori yang tidak berguna.

Ringkasan

Dalam artikel ini saya memperkenalkan beberapa pengetahuan asas tentang Buffer, termasuk API dan sifat biasa Buffer, saya harap pengetahuan ini dapat membantu kerja anda.

Untuk lebih banyak pengetahuan berkaitan nod, sila lawati: tutorial nodejs!

Atas ialah kandungan terperinci Artikel ini akan memberi anda pemahaman yang mendalam tentang kelas Buffer dalam Node. Untuk maklumat lanjut, sila ikut artikel berkaitan lain di laman web China PHP!

Kenyataan:
Artikel ini dikembalikan pada:juejin.cn. Jika ada pelanggaran, sila hubungi admin@php.cn Padam