Node.js API及常用第三方模块

一、http模块: http.createServer(function(req,res){}) 二、NodeJS的模块: 1. exports.方法 = 方法 2. module.exports = {} 3. const 本地变量 = require('js地址') 三、 fs模块 1. fs.readFile("文件路径",function(err,data){}) 四、全局变量 1. __dirname 2. __filename 五、path模块: 1. path.join() 2. path.resolve() 3. path.extname() 六、 mime模块(第三方模块) 1. mime.getType() : 文档类型 res.writeHead(200, { "Content-type": mime.getType('.jpg') }); 七、 url模块 // http://www.yts.com/api/index.html?username=rose&type=flower 1. const result = url.parse(url地址) 2. result.pathname ---> api/index.html 3. result.query ----> "username=rose&type=flower"

HTTP 模块

所有后端动态语言要想运行起来,都得先搭建服务器。Node.js 搭建服务器需要用到一个原生的模块 http。

  1. 加载 http 模块
  2. 调用 http.createServer() 方法创建服务,方法接受一个回调函数,回调函数中有两个参数,第一个是请求体,第二个是响应体。
  3. 在回调函数中一定要使用 response.end() 方法,用于结束当前请求,不然当前请求会一直处在等待的状态。
  4. 调用 listen 监听一个端口。
//原生模块
var http = require('http');

http.createServer(function(reqeust, response){
	response.end('Hello Node');
}).listen(8080);

参数接受 -- GET

当以 GET 请求服务器的时候,服务器可以通过 request.mothod 来判断当前的请求方式并通过 request.url 来获取当前请求的参数。

var http = require('http');
var url = require('url');
 
http.createServer(function(req, res){
    var params = url.parse(req.url, true).query;
    res.end(params);
 
}).listen(3000);

参数接受 -- POST

不同于 GET 请求,POST 请求不能通协 url 进行获取,这个时候就需要用到请求体的事件进行监听获取

var http = require('http');
var util = require('util');
var querystring = require('querystring');
 
http.createServer(function(req, res){
    // 定义了一个post变量,用于暂存请求体的信息
    var post = '';     
 
    // 通过req的data事件监听函数,每当接受到请求体的数据,就累加到post变量中
    req.on('data', function(chunk){    
        post += chunk;
    });
 
    // 在end事件触发后,通过querystring.parse将post解析为真正的POST请求格式,然后向客户端返回。
    req.on('end', function(){    
        post = querystring.parse(post);
        res.end(util.inspect(post));
    });
}).listen(3000);

NET 模块

网络模型

net、dgram、http、https

Node内置的模块,对应的网络通信方式

模块 服务
net TCP
dgram UDP
http HTTP
https HTTPS

传输层

网络层

链路层

驱动

物理层

将二进制的0和1和电压高低,光的闪灭和电波的强弱信号进行转换

输入url到页面加载都发生了什么事情?

输入url到页面加载都发生了什么事情?

3次握手

4次挥手

输入地址
浏览器查找域名的 IP 地址
这一步包括 DNS 具体的查找过程,包括:浏览器缓存->系统缓存->路由器缓存...
浏览器向 web 服务器发送一个 HTTP 请求
服务器的永久重定向响应(从 http://example.com 到 http://www.example.com)
浏览器跟踪重定向地址
服务器处理请求
服务器返回一个 HTTP 响应
浏览器显示 HTML
浏览器发送请求获取嵌入在 HTML 中的资源(如图片、音频、视频、CSS、JS等等)
浏览器发送异步请求

状态码

1XX 2XX 3XX 4XX 5XX
信息性状态码 成功状态码 重定向 客户端错误状态码 服务端错误状态码
少见 200 OK 301 永久性重定向 400 请求报文语法错误 500服务器请求错误
204 响应报文不含实体的主体部分 302 临时性重定向(负载均衡) 401发送的请求需要有通过 HTTP 认证的认证信息 307 和302含义相同 503 服务器暂时处于超负载或正在停机维护,无法处理请求
206 范围请求 303 资源存在着另一个 URL,应使用 GET 方法定向获取资源 403 对请求资源的访问被服务器拒绝
304 客户端已经执行了GET,但文件未变化。 404 服务器上没有找到请求的资源

http/https

http是应用层协议,建立在TCP/IP之上,https则建立在TLS、SSL加密层协议之上,现代web基本都是http/https应用。TCP在建立连接要发送报文,http也是,http报文分为请求报文和响应报文,报文格式如下:

对应的代码如下,注意换行和空格

HTTP/1.0 200 OK    //起始行

Content-type:text/plain    //头部
Content-length:19            //头部  

Hi I'm a message!    //主体

Node中http模块提供创建基于http协议的网络通信应用的接口,继承于net模块,采用事件驱动机制,能与多个客户端保持连接,并不为每个连接开启新的进程或线程,低内存、高并发,性能优良。

API

创建TCP、UDP客户端和服务端

在Node中,net模块提供创建基于TCP协议的网络通信的API。

首先引入内置net模块

var net = require("net")
方法 作用
net.createServer([options][, connectionListener]) 创建一个 TCP 服务器。 参数 connectionListener 自动给 'connection' 事件创建监听器。
net.connect(options[, connectionListener]) 返回一个新的 'net.Socket',并连接到指定的地址和端口。 当 socket 建立的时候,将会触发 'connect' 事件。
net.createConnection(options[, connectionListener]) 创建一个到端口 port 和 主机 host的 TCP 连接。 host 默认为 'localhost'。
net.connect(port[, host][, connectListener]) 创建一个端口为 port 和主机为 host的 TCP 连接 。host 默认为 'localhost'。 参数 connectListener 将会作为监听器添加到 'connect' 事件。返回 'net.Socket'。
net.createConnection(port[, host][, connectListener]) 创建一个端口为 port 和主机为 host的 TCP 连接 。host 默认为 'localhost'。 参数 connectListener 将会作为监听器添加到 'connect' 事件。返回 'net.Socket'。
net.connect(path[, connectListener]) 创建连接到 path 的 unix socket 。参数 connectListener 将会作为监听器添加到 'connect' 事件上。返回 'net.Socket'。
net.createConnection(path[, connectListener]) 创建连接到 path 的 unix socket 。参数 connectListener 将会作为监听器添加到 'connect' 事件。返回 'net.Socket'。
net.isIP(input) 检测输入的是否为 IP 地址。 IPV4 返回 4, IPV6 返回 6,其他情况返回 0。
net.isIPv4(input) 如果输入的地址为 IPV4, 返回 true,否则返回 false。
net.isIPv6(input) 如果输入的地址为 IPV6, 返回 true,否则返回 false。

net.Server

net.Server通常用于创建一个TCP或本地服务器。

方法 作用
server.listen(port[, host][, backlog][, callback]) 监听指定端口 port 和 主机 host ac连接。 默认情况下 host 接受任何 IPv4 地址(INADDR_ANY)的直接连接。端口 port 为 0 时,则会分配一个随机端口。
server.listen(path[, callback]) 通过指定 path 的连接,启动一个本地 socket 服务器。
server.listen(handle[, callback]) 通过指定句柄连接。
server.listen(options[, callback]) options 的属性:端口 port, 主机 host, 和 backlog, 以及可选参数 callback 函数, 他们在一起调用server.listen(port, [host], [backlog], [callback])。还有,参数 path 可以用来指定 UNIX socket。
server.close([callback]) 服务器停止接收新的连接,保持现有连接。这是异步函数,当所有连接结束的时候服务器会关闭,并会触发 'close' 事件。
server.address() 操作系统返回绑定的地址,协议族名和服务器端口。
server.unref() 如果这是事件系统中唯一一个活动的服务器,调用 unref 将允许程序退出。
server.ref() 与 unref 相反,如果这是唯一的服务器,在之前被 unref 了的服务器上调用 ref 将不会让程序退出(默认行为)。如果服务器已经被 ref,则再次调用 ref 并不会产生影响。
server.getConnections(callback) 异步获取服务器当前活跃连接的数量。当 socket 发送给子进程后才有效;回调函数有 2 个参数 err 和 count。
let server = net.createServer((socket) => {});
server.listen(3000, () => {});

net.Socket事件

net.Socket对象是 TCP 或 UNIX Socket 的抽象。net.Socket 实例实现了一个双工流接口。 他们可以在用户创建客户端(使用 connect())时使用, 或者由 Node 创建它们,并通过 connection 服务器事件传递给用户。

方法 作用
lookup 在解析域名后,但在连接前,触发这个事件。对 UNIX sokcet 不适用。
connect 成功建立 socket 连接时触发。
data 当接收到数据时触发。
end 当 socket 另一端发送 FIN 包时,触发该事件。
timeout 当 socket 空闲超时时触发,仅是表明 socket 已经空闲。用户必须手动关闭连接。
drain 当写缓存为空得时候触发。可用来控制上传。
error 错误发生时触发。
close 当 socket 完全关闭时触发。参数 had_error 是布尔值,它表示是否因为传输错误导致 socket 关闭。
let server = net.createServer((socket) => {
    socket.on('data', (data) => {});
    socket.on('end', () => {});
    socket.on('error', (err) => {});
    socket.on('close', () => {});
});
server.on('close', (socket) => {});
server.on('error', (e) => {});

net.Sockets属性

net.Socket提供了很多有用的属性,便于控制socket交互:

socket.connect(path[, connectListener]) 打开指定路径的 unix socket。通常情况不需要使用 net.createConnection 打开 socket。只有你实现了自己的 socket 时才会用到。
socket.setEncoding([encoding]) 设置编码
socket.write(data[, encoding][, callback]) 在 socket 上发送数据。第二个参数指定了字符串的编码,默认是 UTF8 编码。
socket.end([data][, encoding]) 半关闭 socket。例如,它发送一个 FIN 包。可能服务器仍在发送数据。
socket.destroy() 确保没有 I/O 活动在这个套接字上。只有在错误发生情况下才需要。(处理错误等等)。
socket.pause() 暂停读取数据。就是说,不会再触发 data 事件。对于控制上传非常有用。
socket.resume() 调用 pause() 后想恢复读取数据。
socket.setTimeout(timeout[, callback]) socket 闲置时间超过 timeout 毫秒后 ,将 socket 设置为超时。
socket.setNoDelay([noDelay]) 禁用纳格(Nagle)算法。默认情况下 TCP 连接使用纳格算法,在发送前他们会缓冲数据。将 noDelay 设置为 true 将会在调用 socket.write() 时立即发送数据。noDelay 默认值为 true。
socket.setKeepAlive([enable][, initialDelay]) 禁用/启用长连接功能,并在发送第一个在闲置 socket 上的长连接 probe 之前,可选地设定初始延时。默认为 false。 设定 initialDelay (毫秒),来设定收到的最后一个数据包和第一个长连接probe之间的延时。将 initialDelay 设为0,将会保留默认(或者之前)的值。默认值为0。
socket.address() 操作系统返回绑定的地址,协议族名和服务器端口。返回的对象有 3 个属性,比如{ port: 12346, family: 'IPv4', address: '127.0.0.1' }。
socket.unref() 如果这是事件系统中唯一一个活动的服务器,调用 unref 将允许程序退出。如果服务器已被 unref,则再次调用 unref 并不会产生影响。
socket.ref() 与 unref 相反,如果这是唯一的服务器,在之前被 unref 了的服务器上调用 ref 将不会让程序退出(默认行为)。如果服务器已经被 ref,则再次调用 ref 并不会产生影响。

new net.Socket([options])构造一个新的socket对象。

let server = net.createServer((socket) => {
    socket.setEncoding('utf8');
    socket.write();
    socket.end();
});

TCP模拟HTTP

可以用net模块模拟出http模块的功能

我们还可以在这里可以借助socket.pipe配合fs.createWriteStream来吧浏览器的数据保存到本地message.txt文件中,

在浏览器输入http://localhost:3000会返回hello

let net = require('net');
let server = net.createServer({
    // 如果 pauseOnConnect 被设置为 true, 那么与连接相关的套接字都会暂停,也不会从套接字句柄读取数据。 这样就允许连接在进程之间传递,避免数据被最初的进程读取。 如果想从一个暂停的套接字开始读数据
    pauseOnConnect: true
}, (socket) => {
    socket.setEncoding('utf8');
    socket.on('data', (data) => {
        console.log(data);
    });
    socket.on('end', () => {
        console.log('client disconnected');
    });
    //接收到客户端发送的错误就会调用   
    socket.on('error', (err) => {
        console.log("error");
    });
    socket.on('close', () => {
        console.log("close socket");
    });
    socket.end(`
HTTP/1.1 200 OK
Content-Type: text/plain
Content-Length: 5

hello`)
    console.log('request');
});

server.listen(3000, () => {
    console.log('opened server on', server.address({}));
});
server.on('connection', (socket) => {
    console.log('connection');
});

//server.unref();//停止node对server的监听事件
//服务器关闭事件
server.on('close', (socket) => {
    console.log('close server');
});
server.on('error', (e) => {
    if (e.code === 'EADDRINUSE') {
        console.log('Address in use, retrying...');
        setTimeout(() => {
            server.close();
            server.listen(PORT, HOST);
        }, 1000);
    }
});

postman测试,记录在message.txt的格式为

POST /abc HTTP/1.1
Content-Type: multipart/form-data; boundary=--------------------------879095998142409176007484
abc: 123
bbb: ccc
ddd: eee
token: eyJkYXRhIjp7ImlucHV0RW1haWwiOiJsZW1vbiIsImlucHV0UGFzc3dvcmQiOiIxMjMifSwiY3JlYXRlZCI6MTU0NzA0MTEyMCwiZXhwIjo2MH0=.jP/Nm3RMzy6MGnH5uHWuvTkFZp94Bm5tMfDhhdRxlaM=
cache-control: no-cache
Postman-Token: 97b4950a-1169-407b-8787-ab238d3954d4
User-Agent: PostmanRuntime/7.6.0
Accept: */*
Host: localhost:3000
cookie: csrfToken=58RWUaRa3ZuA2uIp7cxn34pC
accept-encoding: gzip, deflate
content-length: 157
Connection: keep-alive

----------------------------879095998142409176007484
Content-Disposition: form-data; name="x"

x
----------------------------879095998142409176007484--

net源码中我们也可以看到这些设置,从中我们得知http模块是真的基于net模块实现的

var statusLine = `HTTP/1.1 ${statusCode} ${this.statusMessage}${CRLF}`; // line 252

function Server(options, requestListener) {
  net.Server.call(this, { allowHalfOpen: true });
  if (requestListener) {
    this.on('request', requestListener);
  }
} // line 283

net.Server.call(this, { allowHalfOpen: true }); //line 298

参考文档