资源
各种进阶资源 + 联系方式请看下方链接
资源
Buffer
- 在Node中,应用需要处理网络协议、操作数据库、处理图片、接收上传文件等,在网络流和操作文件过程中还需要处理大量二进制数据,js自有的字符串远远无法满足这些需求,所以Buffer对象应运而生
- Buffer是一个像Array的对象,但他主要用来操作字节,它是js与C++结合的模块,性能部分用C++实现,非性能相关部分用js实现
- Buffer所使用的内存不是V8分配的,属于堆外内存,由于Node进程开始就加载了它,所以Buffer在全局对象Gloabal上,使用的时候可以直接使用不用require,可以通过Buffer把别的形式 如字符串转换为二进制形式的
- 给Buffer[下标]进行赋值 如果小于0则依次加256直到得到0-255之间的数为止,如果大于255就逐次减256得到0-255之间得数,如果有小数就舍去小数部分
- Buffer的内存分配
- Buffer是用的是C++申请内存,js使用内存的策略
- 可以功过slab动态规定Buffer内存,分配的内存有三种状态,完全分配状态,部分分配状态,没有被分配状态
- 如果申请了一个内存空间位1MB 第一个buffer对象的内存为2kb第二个为不超过1024-2kb的时候 他们都会在这个内存空间中,但是如果第二个超过了第一个内存空间就被2kb独占了 第二个buffer对象会重新申请新的内存空间
- 小而频繁的Buffer操作可以使用slab进行操作 大的Buffer对象,直接使用C++层面提供的内存 无需细腻操作
- Buffer转换
- buffer可以与其他类型进行转换 如 字符串类型 但是也不是所有类型都可以进行转换,可以用isEncoding()函数来判断编码是否支持转换
- 对于不支持类型的转换可以借助node的生态圈中的模块进行转换,例如iconv-lite模块是纯js支持的性能比C++更好
- buffer转换过程中 流是被逐步读取的 如果是英文还好说 中文是占3个字节的 如果设置了buffer的长度 例如长度为5很可能第二个中文传输过来就会出现乱码的情况
- 乱码情况可以通过setEncoding(encoding) 函数解决该函数的作用是让data事件中传递的不再是一个buffer对象而是一个编码后的字符串因为setEncoding()函数内部使用了string_decoder模块 这个模块可以截留之前被劫断的中文字符然后接收到下个流的时候进行对接 但是目前只能解决UTF-8、base64、和UCS-2/UTF-16LE三种编码
- Buffer可以通过 buffer.concat()实现了从小Buffer面向大Buffer复制的过程
Buffer.concat = function(list, length) { if (!Array.isArray(list)) { throw new Error('Usage: Buffer.concat(list, [length])'); } if (list.length === 0) { return new Buffer(0); } else if (list.length === 1) { return list[0]; } if (typeof length !== 'number') { length = 0; for (var i = 0; i < list.length; i++) { var buf = list[i]; length += buf.length; } } var buffer = new Buffer(length); var pos = 0; for (var i = 0; i < list.length; i++) { var buf = list[i]; buf.copy(buffer, pos); pos += buf.length; } return buffer; };
- Buffer与性能
- Buffer在文件I/O和网络I/O中用途广泛,尤其是网络传输中,它的性能举足轻重,在web应用中字符串转换为Buffer是时时刻刻发生的,提高字符串到Buffer的转换效率,可以很大程度提高网络吞吐率
- 通过预先转换静态资源为Buffer对象,可有效减少cpu重复使用,节省服务器资源,所以使用Node构建web应用的时候可以使静态动态资源分离,静态资源转为Buffer使性能得到提升。
- 读取一个相同的大文件时,highWaterMark值的大小与读取速度的关系:该值越大读取速度越快
网络编程
- 在WEB领域,大多数的编程语言都需要专门的web服务器作为容器,如ASP、ASP.NET需要IIS作为服务器,PHP需要Apache或Nginx环境等,但对于Node而言,之需要几行代码即可构建服务器,无需额外的容器。Node提供了net、dgram、http、https这4个模块,分别用于处理TCP、UDP、HTTP、HTTPS适用于服务器端和客户端,TCP server(服务端) TCP client(客户端)
- 构建TCP服务:TCP服务在网络应用中十分常见,目前大多数应用都是基于TCP搭建而成的
-
TCP全名为传输控制协议,在OSI模型( 由七层组成,分别为物理层、数据链接层、传输层、会话层、表示层、应用层)中属于传输层协议。许多应用层协议基于TCP构建,典型的是HTTP、SMTP、IMAP等协议 如下图所示
-
TCP是面向连接的协议,其显著特征是传输之前需要三次握手才能形成会话,只有会话形成后,服务器端和客户端才能互相发送数据。在创建会话的过程中,服务器端和客户端分别提供一个套接字,这两个套接字共同形成一个连接。服务器端与客户端则通过套接字实现两者之间连接的操作。
-
- TCP服务的事件(里面的套接字=IP地址+端口)
-
服务器事件:对于通过net.cerateServer()创建的服务器而言,他是一个EventEmitter实例,它的自定义事件有如下几种。
listening:在调用server.listen()绑定端口或者 Domain Socket后触发
connectio:每个客户端套接字连接到服务器端时触发
close:当服务器关闭时触发
error:服务器发生异常时触发 -
连接事件:服务器可以同时与多个客户端保持连接,对每个连接而言是典型的stream对象它可以用于两端的通信,即可通过data事件从一端读取另一端发来的数据,也可通过write()方法从一端向另一端发送数据。它有如下自定义事件:
data:当一端调用write()发送数据时,另一端会触发data事件,事件传递的数据既是write()发送的数据。
end:当连接中的任意一端发送了FIN数据时,将会触发该事件。
connect:该事件用于客户端,当套接字与服务器端连接成功时会触发
drain:当任意一端调用write()发送数据时,当前这段会触发该事件
error:当异常发生时触发
close:套接字完全关闭时触发
timeout:当一定时间后连接不在活跃时,该事件会被触发,通知用户当前连接已经被闲置了
-
- TCP针对网络中的小数据包有一定的优化策略:Nagle算法:只有缓冲区的数据达到一定数量或者一定时间后才将其发出,所以数据可能被延迟发送可调用socket.setNoDelay(true)去掉Nagle算法但是在网络另一端调用write()会触发另一端的data()事件,但是并不意味着每次write()都会触发一次data事件,另一端也可能接收到多个小数据包合并,然后只触发一次data事件
- 构建UDP服务
- TCP中连接一旦建立,所有的会话都基于连接完成,客户端如果要与另一个TCP服务通信,需要另创建一个套接字完成连接,但在UDP中,一个套接字可以与多个UDP服务通信,它提供面向事务的高效的简单不可靠的数据传输服务,例如视频,音频之类的,DNS服务也是基于它实现的
- 创建UDP套接字十分简单,UDP套接字一旦创建,既可以作为客户端发送数据,也可以作为服务器端接收数据。如下所示
var dgram = require('dgram');
var socket = dgram.createSocket("udp4");
//UDP客户端与服务端对话
var dgram = require('dgram');
var message = new Buffer("深入浅出Node.js");
var client = dgram.createSocket("udp4");
client.send(message, 0, message.length, 41234, "localhost", function(err, bytes) {
client.close();
});
//保存为client.js并执行,服务器端的命令行将会有如下输出
$ node server.js
server listening 0.0.0.0:41234
server got: 深入浅出Node.js from 127.0.0.1:58682
- UDP套接字事件如下:
message:UDP套接字侦听网卡端口后,接收到消息时触发该事件,触发携带的数据为消息Buffer对象和一个远程地址信息
listening:当UDP套接字开始侦听时触发该事件。
close: 调用close()方法时触发该事件,并不在触发message事件。如需再次触发message事件,重新绑定即可
error:当异常发生时触发该事件,如果不侦听,异常将直接抛出,使进程退出。
- 构建HTTP服务
- TCP与UDP都属于网络传输层协议,如果要构造高效的网络应用,就应该从传输层进行着手。
- HTTP构建在TCP上,属于应用层协议,在HTTP两端是服务器与客户端,即著名的B/S模式,从协议的角度来说现在的浏览器其实就是一个HTTP代理,用来处理用户与服务器端的交互。
- HTTP只做两个事情:处理HTTP请求和发送HTTP响应他们都包含报文头和报文体。
- Node的HTTP模块包含对HTTP处理的封装,他能够与多个客户端保持连接,由于其采用的事件驱动的形式,并不为每个连接创建额外的线程或进程,保持很低的内存占用,所以能实现高并发
- TCP与HTTP的区别是 TCP经过三次握手一旦建立连接除非主动经过四次握手断开否则可以看做一个长连接 而http建立的连接可以看做是一个短连接 他们都可以用于多次请求和响应
- 如同TCP服务一样,HTTP服务器也抽象了一些事件,以供应用层使用,同样典型的是,服务器也是一个EventEmitter实例
connection事件:开始HTTP请求和响应前,客户端与服务器端需要建立底层的TCP连接,这个连接可能因为开启了keep-alive,可以在多次请求响应之间使用;当这个连接建立时,服务器触发一次connection事件
request事件:建立TCP连接后,http模块底层将在数据流中抽象出HTTP请求和相应,当请求数据发送到服务器端,在解析出http请求头后,将会触发该事件;在res.end()后,TCP连接可能将用于下一次请求响应。
close事件:与TCP服务器的行为一致,调用server.close()方法停止接受新的连接,当已有的连接都断开时,触发该事件;可以给server.close()传递一个回调函数来快速注册该事件。
checkContinue事件:默写客户端在发送比较大的数据时服务器端触发
connect事件:当客户端发起CONNECT请求时触发,而发起该请求通常在HTTP代理时出现;如果不监听该事件,发起该请求的连接将会关闭。
upgrade事件:当客户端要求升级协议时触发,如websoket
clientError事件:连接的客户端触发error事件,这个错误会传递到服务端此时触发该事件。 - HTTP客户端的响应与服务器端较为类似,在ClientRequet对象中,它的事件叫做response。ClientRequest在解析响应报文时,一解析完响应头就触发response事件,同时传递一个响应对象用于操作ClientRequest,后续响应报文体以只读流的方式提供。
- HTTP代理:http提供的ClientRequest对象也是基于TCP层实现的,在keepalive的情况下,一个底层会话连接可以多次用于请求。。http模块包含一个默认的客户端代理对象http.globalAgent。他对每个服务器端创建的连接进行管理,默认情况下,通过ClientRequest对象对同一个服务器端发起的HTTP请求最多可以创建5个连接,它的实质上是一个连接池,所以不管一个客户端同时对一个服务器同时发起几次请求,其实质只有5个请求可以出于并发状态后续的需要某个请求完成才可触发,当然这个可以自定义
- HTTP客户端事件
response:接收到服务器端响应时触发
socket:当底层连接池中建立的连接分配给当前请求事件时触发该事件。
connect:当客户端向服务端发起connect请求时,如果服务器端响应了200状态码,客户端将会触发该事件。
upgrade:客户端向服务器端发送upgrade请求时,如果服务器端响应了101状态 客户端会触发该事件
continue:客户端向服务器端发送大数据量如果服务器端响应100则触发该事件