想过深入了解Node.js Buffer 么?一探究竟吧 !
发布于 9 个月前 作者 yangheng 2756 次浏览 来自 分享

background

你是否像我一样在用Node.js 时候,遇到 Buffer ,Streambinary data 时候就发懵?感觉这些是Node.js大佬们或者是npm上开发package的大神才会需要搞的懂的东西?

确实,这些个术语看起来有点吓人,特别是当你没有汇编基础就从事web Node.js开发的时候.

还有比较操蛋的就是现在许多教程将node.js核心部分的讲解以及为他们存在的原由直接跳过,直接教你如用package去开发web应用。甚至有些更臭不要脸的告诉你你不需要太深究这些内容你只需要会用就可以,因为你根本就不会直接和他们打交道。

当然,如果你只是想成为一个普通的node开发者的话,你确实不需要这些。

但是,如果这些神秘的技术使你感到好奇,且好奇心驱使着你探索不止于现状想要成为一个node大神的话,那么你就要深入理解像Buffer 类似的node的核心功能模块。这就是我要写这篇文章的目的,就是来帮助你更好的理解这些核心功能,让我们的node.js技术更上一层楼。

在介绍Buffer 之前,看下官网的对这部分的描述。。。

​ … mechanism for reading or manipulating streams of binary data. The Buffer class was introduced as part of the Node.js API to make it possible to interact with octet streams in the context of things like TCP streams and file system operations.

嗯哼,除非你能理解上面这段话的所有词的意思。这都是写官方的术语。让我们试着把意思给简化下,描述的更明白点,别被这些花里胡哨的东西给带懵了。从这段描述里,我们可以非常靠谱的说:

Buffer 这个存在的意义就是用来操作binary data(二进制数据)streams(流)的 。

怎么样描述够简单了吧?但是,Buffer,binary data,streams这些词……。好吧,我们从后向前一个个的解释。

Binary data, 究竟是什么?

你可能已经知道计算机是用二进制形式来存储和表示数据的。二进制就简单的0和1的集合。如下是的5个不同的二进制数据,5个不同的0,1集合:

10 ,01,001,1110,00101011

在二进制里的每一位(1或者0)都叫Bit(比特位)。

为了存储或表示一块数据,计算机需要把对应的数据转成二进制表示形式。例如,计算机要想存储数字12,得把12转成它对应的二进制表示形式 1100

那么计算机是如何知道这种转化呢?好吧,这是纯数学计算了,就是我们在基础数学里面学过的二进制数字计算。计算机是能理解这种数学计算的。

但是我们平时用到的可不止数字啊。还有字符串,图像,视频。那么计算机是如何用二进制表示这些内容的呢?拿字符串来举个例子。计算机是如何把字符串“L” 用二进制形式表示的呢?首先计算机会把字符串L转成数字表示的L.然我们看看它是怎么做到的?

打开浏览器控制台,粘贴如下的代码并按*“回车”."L".charCodeAt(0)。看出现了什么?是76么?对76就是L的对应的字符编码。那么计算机是如何知道什么数字代表每一个字符呢?计算机咋就知道76就是表示的L*?

Character Sets

Character Sets 定义好了数字和字符之间的对应关系。而且已经有了很多不同的定义规则。最常用的就是UnicodeASCII。JavaScript就很好的诠释了Unicode 。在Unicode 编码里76就是代表了L

由此我们可知计算机是如何用数字表示字符的了。现在就算几也要相应的把76转成二进制表示形式。你可能认为就是把76转成基本的2进制数字嘛,其实并没有那么快。

## Character Encoding

就像那些规则里定义了哪些数字表示了哪些字符一样,这些规则也定义了这些数字应该如何用二进制表示。特别是,用多少比特位来表示这些数字呢?这就叫Character Encoding(字符编码)了。

UTF-8就是其中之一。UTF-8定义了字符用**字节(byte)**来编码。字节就是8比特位(8个1或0)。也就是8个0或者1来表示二进制的任何字符。

为了更易于理解,之前提到的 12用二进制表示就是 1100。当用UTF-8来表示12的时候却是用8比特位,不足8比特位时计算机就会在左侧增加比特位补齐到8位。因此12UTF-8编码的存储结果就是00001100。怎么样,有点感觉了么?

所以呢,76最终存储为01001100

这就是计算机如何用二进制存储字符串或者字符的了,朋友们。

类似的,计算机也定义了如何转化,编码以二进制形式存储图像和视频的了。这里的重点是,计算机将所有数据类型存储在二进制文件中,这就是所谓的二进制数据。

如果你特别对字符编码的幕后原理感兴趣的话,你可能喜欢这个篇文章。 这篇文章里做了详细的介绍.

现在我们也了解了二进制了,那么之前提到的**二进制流(streams of binary data)**是啥呢?

Stream

Stream在Node.js里就是简单讲就是即将在空间里被移动的一系列数据。完整的概念就是,你要处理许多数据,但是又无需去等待这些数据在此之前全部已经准备好。

就是说把一个大的数据切割成一个个小块(chunk)来发送。原始的Buffer(“二进制streams…在上下文环境… 文件系统”)定义就可以简单理解为文件系统中的被移动的二进制数据。例如吧text1文件中的内容移到text2中。

但是Buffer 又是如何帮我们和二进制流数据打交道的呢?顺便说下Buffer 准确的来讲是啥呢?

Buffer

我们已经知道了数据流就是从一点到另一个点移动的数据。那么他们是怎么动的呢?

通常来说,这些移动的数据要么是等待被处理的或者是被读出来或者其他什么决定。但是一定时间里系统处理的数据是有上限和下限的。因此如果数据到达的速率比处理的速率快的话,这些剩下的数据就要去等待进程按序处理。

还有一种情况呢,如果进程处理的速度比数据达到的速度要快的话,之前早到的数据就得等到凑够了一定量的数据才能被送到处理进程里。

这个*“等待区域”就是Buffer*.通常是在计算机RAM中的一个物理地址,里面的数据是在数据流期间暂时收集起来的,被发送给进程处理。

我们可以吧整个的streamBuffer的进程比喻成公共汽车站。有的公共汽车站就是必须要等到乘客到达一定数量或者到了发车时间的时候才可以开车离开。乘客可能是不同时间以不同速度达到车站的,乘客和车站都无法控制乘客的到站时刻。

一种情况是,早到的乘客就得等着车站按照车站的规则发车。还有就是车已经发动或者离开之后才到的乘客就得等下一辆车。

不论哪种情况,都会有等候的地方。这对Node.js来说就是Buffer。Node.js无法控制数据的流的传输速度和数据的到达时间,它只能决定何时能把数据发送出去。如果时机不到,就得把输入存到bufffer(RAM中的一个很小的物理地址)里,直到合适的时间把数据发到处理进程中去。

常见的案例Buffer就是在线视频。如果你的网速足够快的话,视频流的速度就足够快到能充满buffer,发送给处理进程,然后充满下一个,以此类推,直到这个**流(Stream)**处理完毕。

如果你的网速慢的话,处理完已经达到的数据之后,播放器就会出现个loading,这就是说在收集更多的数据或者是等着的数据到达一定数量。只有当Buffer填满了才会交给播放器来处理。播放器才会出内容。在播放的时候,相继的会收到有更多数据,存在Buffer里等着。

如果播放器已经处理完了或者播放之前的内容,这个buffer还没有充满,就会再次出现loading,等着收集更多数据来处理。

这就是Buffer!

在最初的Buffer定义里,只是告诉我们z哎buffer里,我们能操作移动的二进制数据。我们可能会有哪些方式处理底层的二进制数据呢?在Node.js实现的Buffer提供了完整可行的清单。

Interacting with a Buffer

创建自己的buffer都有可能。Node.js除了在Stream中自动创建Buffer之外呢,我们可以自己造自己的Buffer.牛逼不牛逼不?

根据不同的需求,有多种创造方式。

// Create an empty buffer of size 10. 
// A buffer that only can accommodate 10 bytes.
const buf1 = Buffer.alloc(10);
// Create a buffer with content
const buf2 = Buffer.from("hello buffer");

一旦Buffer建完了,就可以和它交互了。

// Examine the structure of a buffer
buf1.toJSON()
// { type: 'Buffer', data: [ 0, 0, 0, 0, 0, 0, 0, 0, 0, 0 ] }
// an empty buffer
buf2.toJSON()
// { type: 'Buffer',
     data: [ 
       104, 101, 108, 108, 111, 32, 98, 117, 102, 102, 101, 114 
     ] 
   }
// the toJSON() method presents the data as the Unicode Code Points of the characters
// Examine the size of a buffer
buf1.length // 10
buf2.length // 12. Auto-assigned based on the initial content when created.
// Write to a buffer
buf1.write("Buffer really rocks!") 

// Decode a buffer
buf1.toString() // 'Buffer rea'
//oops, because buf1 is created to contain only 10 bytes, it couldn't accommodate the rest of the characters
// Compare two buffers

可以和Buffer做更多的操作。在 在官网文档里有更多使用的方法。

最后,给你留一个小小的挑战:去读node.js 核心库之一 zlib.js源代码,看看它是如何借助Buffer强大功能处理二进制数据流的。这些都是gzip文件,当你阅读的时候,记得把所得所感写在评论里与我们分享下。

我希望这篇文章能帮助你更好的理解node.js里面的Buffer.

如果你觉得我做的不错的话,可以帮我在node.js社区里广为扩算下,让更多人看到,更多的人能有个更好的理解。

如果你对此有任何不同的理解,请在评论中指出。

原文摘自 freamcodeCamp

16 回复

这个编辑器,我日了。markdown 最基本的语法都不支持么?

@yangheng 这md写的也是醉了,请按照标准写法

可以说,翻译的很有个人风格。

@i5ting 我是标准写法,在typora上直接复制过去的。。。

@yangheng 瞎说,#号后面是一个空格?

@i5ting 本来就不存在标准Markdown 这种东西吧。如果你说的标准Markdown 是github牵头然后被Markdown 之父怼后改名的common Markdown 他的教程的标题写法是有空格的。

我用过github,简书,cnode的md好像三者都有不一样的地方。有的要加空格,有些不需要

@yangheng @i5ting @Lynskylate @fruit-memory 不是编辑器的原因,是 cnode 所使用的 https://github.com/markdown-it/markdown-it 库的表现行为。

buffer是一个无符号8位TypeArray的实现,理解这个含义就差不多了。

赞,讲的很清楚

那么 01001100 什么时候代表76 什么时候又代表 L 呢

@Alexis374 在内存里就是这样存。你 toString() 了就是 ‘L’,你 readInt32 就是 76。

跟在 C 里面一样,printf 的时候 %c%d 的区别。

回到顶部