当前位置: 首页 > 后端技术 > Node.js

NodeChatBuffer

时间:2023-04-03 21:22:59 Node.js

原文地址:http://www.cnblogs.com/DeanCh...刚接触Nodejs的时候,一些概念总是让我这个前端学习者感到困惑(虽然我也是做前端的我上大学时的后端)end,世界上最好的语言,你知道的)。FileSystem、Path等功能明显的模块我可以很快看懂,但是Buffer神秘莫测的东西我一下子看不懂。因为,在前端js实践中,很少去想编码方式,字符集之类的。二元理解仅限于大学课堂。本文与其说是讨论Node的Buffer模块,不如说是从字符集和编码的角度来讨论如何理解建立Buffer模块的意义。Nodechat系列不涉及具体的API讲解,只是概述了一些我认为比较重要的特性。1.基础知识就像我们学习编程的第一课一样,我们知道计算机是一种二进制生物。它只能理解1和0,或者存在和不存在。“太极生两仪,两仪生四象,四象生八卦”。无论我们在计算中看到什么,都是通过一种特殊的编码方式,或者说约定俗成来表现出来的。我们非常熟悉的ASCII码就是这样一种规范。和摩尔斯电码一样,一个固定的值代表一个固定的含义。ASCII码用1字节8位来表示2^8=256种状态。比如大写字母A对应65,B对应66,是不是很简单?ASCII码并没有占据全部256位,只使用了128位中的一半。在一个字节中,只占用最后7位,第一位统一标识为0。但是我们不难发现,ASCII码是远远不够的,至少我们的汉字是表达不了的。后面还考虑了很多其他的解决方案,这里就不赘述了,直接说说最终的解决方案——Unicode。Unicode的想法很直接,就是想把世界上所有的字符都包括进来。我们一般认为Unicode是用两个16位的字节来表示的,完全涵盖了ASCII字符集。比如汉字“好”在unicode中的二进制表示是0101100101111101,转成十六进制就是U597D(U只是表示它们是unicode码)。前面之所以说“一般信念”,是因为这种想法是不准确的。一个Unicode平面是两个字节。我们常说的是它的基本平面,编码是U+0000到U+FFFF,常见的字符都在这个平面上。Unicode还有16个辅助平面,码位范围从U+010000到U+10FFFF。一般来说,我们只需要关注基本平面,习惯Unicode表示即可。因为,这毕竟是各种编码方式之间转换的“硬通货”。我们常说的utf-8和utf-16是什么?这些是具体的编码方式,Unicode是一个字符集。以utf-8为例,它在unicode编码的基础上重新编码,将一些不需要占用2个字节的编码转换为1个字节。比如ASCII中的那些字符,在unicode中,第一个字节全为0,很浪费空间,而且还会把汉字编码成3个字节。你可以在控制台试试Buffer.from('me','utf8')看看编码后的字节数。javascript使用哪种编码?JavaScript使用Unicode字符集,但只支持一种编码方式。那就是USC-2。你没听说过吗?你可以理解为utf-16。但它与UTF-16到底有什么关系呢?两者的关系简单来说就是UTF-16代替了UCS-2,或者UCS-2融入了UTF-16。所以,现在只有UTF-16,没有UCS-2。UCS-2只支持两个字节,而在它之后出现的UTF-16在UCS-2的基础上使用辅助平面可以支持4个字节。由于UCS-2被集成到UTF-16中,所以UTF-16中有一些字符,但UCS-2不存在。遇到这种情况怎么办?您可以参考以下参考资料中阮老师的讲述。2.Buffer生成相关的重要APIBuffer.alloc(size[,fill[,encoding]])Buffer.allocUnsafe(size)Buffer.allocUnsafeSlow(size)Buffer.from(array)buf.fill(value[,offset[,end]][,encoding])在Buffer生成的过程中,最关心的就是内存的申请和分配。原先用newBuffer()生成Buffer的方法不建议再次使用。与Buffer.allocUnsafe()方法一样,它可能包含敏感数据。为什么它会包含敏感数据?在生成buffer的过程中,并不是一步完成的,而是分为两步,1.申请内存空间,2.填充请求的内存空间。Buffer.allocUnsafe()方法只完成了第一步。没有完成第二步的后果就是申请的空间可能会“残留”在之前内存上的数据。毕竟一块内存在电脑中总是申请然后释放的,难免有些数据没有及时清理。当然,由于少了第二步,速度自然要快很多。consta=Buffer.allocUnsafe(10);console.log(a)//打印出来的结果总是不一样,但是我们发现每一位很可能不是00,这些数据是敏感数据。您可以使用buf.fill(0)进行后续填充。但是为了避免bug,你应该避免使用Buffer.allocUnsafe()来分配内存。Buffer.alloc()之所以比Buffer.allocUnsafe()更安全,是因为它在第二步中清除了所有旧数据并用0填充。consta=Buffer.alloc(10);console.log(a)//打印出来的结果每一位都是0,看到这里,是不是觉得两者的区别Buffer.alloc()和Buffer.allocUnsafe()限制是否有填充数据?其实并不是。与Buffer.alloc()真正的区别在于是否填充数据是Buffer.allocUnsafeSlow()。事实证明,使用Buffer.allocUnsafe()分配内存需要一个共享的内部内存池。而Buffer.alloc()和Buffer.allocUnsafeSlow()直接在内存空间中开辟相应大小的内存空间。Buffer.allocUnsafe()(类似于之前的newBuffer(size)机制)是三者中最快的内存分配方式。它采用共享内部内存池的方式,通过预先分配一定大小的一段内存,JavaScript从中分配相应大小的一段内存,避免系统频繁申请内存分配,从而达到更高的效率。共享内存池的默认poolSize为8KB(可重新分配)。只有当要分配的内存小于或等于poolSize的一半时,Buffer.allocUnsafe()才会从共享内存池中分配空间。这里的知识点就到此为止。详细讨论之后,可以考虑写一篇专门的文章来解释一下。参考资料的内容也相当不错。推荐阅读。3.Buffer读写相关的重要API,noAssert])buf.writeUInt16BE(value,offset[,noAssert])buf.writeUInt16LE(value,offset[,noAssert])...只有当buffer可以读写时能否体现其存在的价值。查看Buffer的文档,Buffer的读写方法中有很多以“BE”和“LE”结尾的方法。他们代表什么?Big-endian和little-endian“BE”表示“bigendian”大端字节序,“LE”自然表示“littleendian”小端字节序。什么是字节顺序?我们在描述一个字符的unicode编码时,习惯性的从左往右写。为什么不从右往左写呢?无论是读还是写多个字节,总要有一个顺序,就是“字节序”。大端就是我们经常看到的,高位字节在前,低位字节在后,小端顺序正好相反。为什么要区分大端和小端?不能统一从一个方向全部读写吗?计算机电路先处理低位字节,效率更高,因为计算是从低位字节开始的。因此,计算机的内部处理是小端的。然而,人类仍然习惯于以大端顺序读写。因此,除了计算机内部处理,其他场合几乎都是big-endian,比如网络传输、文件存储等。字节序的处理就一句话:“只有读的时候,必须区分字节序,其他情况不用考虑”。好吧,下面举个实际的例子。varbuf=Buffer.from([1,3,5,7]);//buf.readInt16BE(0)//259从buf中读取16位整数,所以读取到对应的码位到第一个字符是0103转换成十进制是1*16^2+3=259buf.readInt16LE(0)//756//小字符是从右往左读的,第一个字符对应的codepoint是0301换算成十进制就是3*16^2+1=756读写输入是一个逆过程,道理是一样的。//官方例子constbuf=Buffer.allocUnsafe(4);buf.writeUInt8(0x3,0);buf.writeUInt8(0x4,1);buf.writeUInt8(0x23,2);buf.writeUInt8(0x42,3);//打印:console.log(buf);Buffer还有很多有趣的方面需要进一步研究。稍后我会进一步补充这篇文章或者写几篇更深入的文章。待续。..参考资料Unicode和JavaScript详解字符编码笔记:Node.js模块之ASCII、Unicode和UTF-8BufferNode源码分析-buffer理解字节序Buffer/Stream和内存管理