Node.js的核心模块服务器API

举报
tea_year 发表于 2022/03/22 11:45:29 2022/03/22
【摘要】 node的核心模块:node为JavaScript提供了很多服务器级别的API,这些API绝大多数都被包装到了一个具名的核心模块中。例如:1. 文件操作的fs核心模块2. http服务构建的http模块3. path路径操作模块4. os操作系统信息模块等等。。。那么,以后只要是我们说这个模块是一个核心模块,想要使用它的话,就必须引入模块var fs = require(“fs”);例如:我...

node的核心模块:

node为JavaScript提供了很多服务器级别的API,这些API绝大多数都被包装到了一个具名的核心模块中。

例如:

1. 文件操作的fs核心模块

2. http服务构建的http模块

3. path路径操作模块

4. os操作系统信息模块

等等。。。

那么,以后只要是我们说这个模块是一个核心模块,想要使用它的话,就必须引入模块

var fs = require(“fs”);

例如:我们做个获取系统cpu信息的例子

//引入模块
let os = require("os");
//获取当前机器的cpu信息
console.log(os.cpus());
//获取当前机器的内存
console.log(os.totalmem());


自定义模块:

这时候,我们来新建一个文件夹,创建一个js文件,假如说我们创建的这个文件是一个功能模块,那么,我们可以在新的js文件里面使用require()来引用我们自己创建的模块功能,并执行!

在这里我们来说明一下require(),它的作用就是用来加载模块的。

在nodejs中,模块分为三种:

1. 具名的核心模块,例如:fs、http

2. 用户自己编写的文件模块

模块的文件作用域:

在这里我们来说明一下,什么是文件作用域。

其实也就是指,我们在一个js文件里面去命名的一些变量,和值,只是在当前文件起作用,换了另一个文件就不管用了

也就是说外部访问不到内部

内部也访问不到外部

举个例子:

我们在a.js中声明一个变量

我们在b.js中调用a.js中的变量

运行完之后,我们就可以发现:报错,找不到

这也就说明了node中,不像浏览器一样,有全局作用于,node只有模块作用域。

模块作用域之间的通信:

我们如果想要得到另一个模块中的数据,那么我们就需要了解一下require()的另一的作用。

require()的第一个作用:加载文件模块,并执行里面的代码

require()的第二个作用:获取被加载文件模块导出接口对象


每一个模块文件中都提供了一个对象:exports。那么exports默认是一个空对象。

那么我们想要访问另一个模块里面的成员,就需要把需要外部访问的成员挂载到这个exports对象中,然后在另一个js文件中引用。



ip地址和端口号的概念:

那么我们现在应该都知道,所有联网的程序都需要进行网络通信,那么我们现在从屋里角度去看,计算机是不是只有一个物理网卡,而且每一个局域网中,网卡的地址是唯一的。

那么网卡是通过唯一的ip地址来进行定位的

那么我们来说,客户端要和服务器进行通信,我们输入一个网址,比如说我们输入的www.baidu .com这个网址,那其实计算机是不认识这个地址的,时需要一个域名的解析,最终解析成一个ip地址,那么计算机可以根据ip地址定位到我们想要连接的服务器,进行连接。

那么我们现在来考虑一下另一个问题,比如说我们有很多客户端,同时给同一个服务器发送了不同的消息,比如,一个发送的是微信的消息,一个发送的是qq的消息等等,那么这些数据在服务器中如何找到相对应的软件进行接收了?这就使用的每个软件都会有一个自己的端口,端口号。

那么端口号用来定义具体的应用程序

那么我们又有一个问题了,那就是服务怎样获取客户端的端口号?

/*
* 我们可以使用node非常轻松的构建一个web服务
* 在node中提供了一个核心模块:http
* http这个模块的职责就是帮你创建编写服务器的
* */
//1.加载http核心模块
let http = require("http");
/*
* 使用 http.createServer()可以创建一个web服务器
* 返回一个server实例
* */
let server = http.createServer();
/*
* 这时候我们就已经有了一个服务器了,那么,服务器能干嘛?
* 1.提供服务:对数据库服务
* 2.发请求
* 3.接受请求
* 4.处理请求
* 5.给个反馈(发送响应)
* 我们使用request这个参数注册request请求事件,
* 当客户端请求发送过来,就会自动触发服务器的请求事件,然后执行第二个参数:回调处理函数
* request请求的回调函数的参数
* request:可以获取客户端的一些请求信息,例如请求路径
* response:响应对象可以用来给客户端发送一些响应消息
* response对象有一个方法:write,可以用来给客户端发送响应数据
* write可以使用多次,但是最后一次,一定要使用end来结束响应,否则客户端会一直等待
* */
server.on("request",function (request,response) {
console.log("收到客户端的请求了,请求路径是"+request.url);
console.log("请求客户端的地址是:",request.socket.remoteAddress,request.socket.remoteMRTCPPort);

response.write("<h1>aaa</h1>");
response.write("<p>hello node</p>")
//使用end方法告诉客户端,终止响应
response.end();
})
//绑定端口号,启动服务器
server.listen(8888,function () {
console.log("服务器启动成功,我们可以通过http://127.0.0.1/8888来进行访问");
})


端口号的范围是:0--65536之间

在计算机中有一些默认的端口号,我们最好不要去使用

如:http服务的80

那么我们在开发的过程之中最好使用一些好记的如:3000,5000,4000等

可以同时开启多个服务,但是一定要确保不同的服务占用的端口号不一致才行

响应内容的类型:

例如我们响应的数据是中文的时候,我们会发现它会乱码,那么我们就需要了解一下

在服务端默认发送的数据,其实是UTF8编码的内容,

而浏览器在不知道服务器响应编码的情况下会按照当前操作系统的默认编码去解析,那么中文操作系统的默认编码是GBK,

解决方法就是正常的告诉浏览器我给你发送的内容是什么编码

也就是解决响应的中文乱码的问题


let http = require("http");

let server = http.createServer();

server.on("request",function (request,response) {
console.log("收到客户端的请求了,请求路径是"+request.url);
console.log("请求客户端的地址是:",request.socket.remoteAddress,request.socket.remotePort);
response.setHeader("Content-Type","text/html;charset=utf-8");
response.write("<h1>你好</h1>");
response.write("<p>这是我告诉浏览器编码后的效果</p>");
//使用end方法告诉客户端,终止响应
response.end();
})
//绑定端口号,启动服务器
server.listen(8888,function () {
console.log("服务器启动成功,我们可以通过http://127.0.0.1/8888来进行访问");
})




Node事件循环

Node.js 是单进程单线程应用程序,但是因为 V8 引擎提供的异步执行回调接口,通过这些接口可以处理大量的并发,所以性能非常高。

Node.js 几乎每一个 API 都是支持回调函数的。

Node.js 基本上所有的事件机制都是用设计模式中观察者模式实现。

Node.js 单线程类似进入一个while(true)的事件循环,直到没有事件观察者退出,每个异步事件都生成一个事件观察者,如果有事件发生就调用该回调函数.

事件驱动程序

Node.js 使用事件驱动模型,当web server接收到请求,就把它关闭然后进行处理,然后去服务下一个web请求。

当这个请求完成,它被放回处理队列,当到达队列开头,这个结果被返回给用户。

这个模型非常高效可扩展性非常强,因为webserver一直接受请求而不等待任何读写操作。(这也被称之为非阻塞式IO或者事件驱动IO)

在事件驱动模型中,会生成一个主循环来监听事件,当检测到事件时触发回调函数。

整个事件驱动的流程就是这么实现的,非常简洁。有点类似于观察者模式,事件相当于一个主题(Subject),而所有注册到这个事件上的处理函数相当于观察者(Observer)。

Node.js 有多个内置的事件,我们可以通过引入 events 模块,并通过实例化 EventEmitter 类来绑定和监听事件,如下实例:

实例:

// 引入 events 模块

var events = require('events');

// 创建 eventEmitter 对象

var eventEmitter = new events.EventEmitter();

// 创建事件处理程序

var connectHandler = function connected() {

console.log('连接成功。');

// 触发 data_received 事件

eventEmitter.emit('data_received');

}

// 绑定 connection 事件处理程序

eventEmitter.on('connection', connectHandler);

// 使用匿名函数绑定 data_received 事件

eventEmitter.on('data_received', function(){

console.log('数据接收成功。');

});

// 触发 connection 事件

eventEmitter.emit('connection');

console.log("程序执行完毕。");

运行结果如下:

Node应用程序是如何工作的?

在 Node 应用程序中,执行异步操作的函数将回调函数作为最后一个参数, 回调函数接收错误对象作为第一个参数。

接下来让我们来重新看下前面的实例,创建一个 input.txt ,文件内容如下:

百度网址:www.baidu.com

创建 main.js 文件,代码如下:

var fs = require("fs");


fs.readFile('D:/node/input1.txt', function (err, data) {

if (err){

console.log(err.stack);

return;

}

console.log(data.toString());

});

console.log("程序执行完毕");

以上程序中 fs.readFile() 是异步函数用于读取文件。 如果在读取文件过程中发生错误,错误 err 对象就会输出错误信息。

如果没发生错误,readFile 跳过 err 对象的输出,文件内容就通过回调函数输出。

执行以上代码,执行结果如下:


如果我们把代码中input.txt改成input1.txt就会输出错误信息

Node.js enentEmitter

Node.js 所有的异步 I/O 操作在完成时都会发送一个事件到事件队列。

Node.js 里面的许多对象都会分发事件:一个 net.Server 对象会在每次有新连接时触发一个事件, 一个 fs.readStream 对象会在文件被打开的时候触发一个事件。 所有这些产生事件的对象都是 events.EventEmitter 的实例。

eventEmitter

events 模块只提供了一个对象: events.EventEmitter。EventEmitter 的核心就是事件触发与事件监听器功能的封装。

你可以通过require("events");来访问该模块。

// 引入 events 模块

var events = require('events');

// 创建 eventEmitter 对象

var eventEmitter = new events.EventEmitter();

EventEmitter 对象如果在实例化时发生错误,会触发 error 事件。当添加新的监听器时,newListener 事件会触发,当监听器被移除时,removeListener 事件被触发。

下面我们用一个简单的例子说明 EventEmitter 的用法:

//event.js 文件

var EventEmitter = require('events').EventEmitter;

var event = new EventEmitter();

event.on('some_event', function() {

console.log('some_event 事件触发');

});

setTimeout(function() {

event.emit('some_event');

}, 1000);

运行结果

运行这段代码,1 秒后控制台输出了 'some_event 事件触发'。其原理是 event 对象注册了事件 some_event 的一个监听器,然后我们通过 setTimeout 在 1000 毫秒以后向 event 对象发送事件 some_event,此时会调用some_event 的监听器。

EventEmitter 的每个事件由一个事件名和若干个参数组成,事件名是一个字符串,通常表达一定的语义。对于每个事件,EventEmitter 支持 若干个事件监听器。

当事件触发时,注册到这个事件的事件监听器被依次调用,事件参数作为回调函数参数传递。

让我们以下面的例子解释这个过程:

//event.js 文件

var events = require('events');

var emitter = new events.EventEmitter();

emitter.on('someEvent', function(arg1, arg2) {

console.log('listener1', arg1, arg2);

});

emitter.on('someEvent', function(arg1, arg2) {

console.log('listener2', arg1, arg2);

});

emitter.emit('someEvent', 'arg1 参数', 'arg2 参数');

运行结果:

以上例子中,emitter 为事件 someEvent 注册了两个事件监听器,然后触发了 someEvent 事件。

运行结果中可以看到两个事件监听器回调函数被先后调用。 这就是EventEmitter最简单的用法。

EventEmitter 提供了多个属性,如 onemiton 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。

方法:

序号

方法 & 描述

1

addListener(event, listener)
为指定事件添加一个监听器到监听器数组的尾部。

2

on(event, listener)
为指定事件注册一个监听器,接受一个字符串 event 和一个回调函数。

server.on('connection', function (stream) {

console.log('someone connected!');

});

3

once(event, listener)
为指定事件注册一个单次监听器,即 监听器最多只会触发一次,触发后立刻解除该监听器。

server.once('connection', function (stream) {

console.log('Ah, we have our first user!');

});

4

removeListener(event, listener)

移除指定事件的某个监听器,监听器必须是该事件已经注册过的监听器。

它接受两个参数,第一个是事件名称,第二个是回调函数名称。

var callback = function(stream) {

console.log('someone connected!');

};

server.on('connection', callback);

// ...

server.removeListener('connection', callback);

5

removeAllListeners([event])
移除所有事件的所有监听器, 如果指定事件,则移除指定事件的所有监听器。

6

setMaxListeners(n)
默认情况下, EventEmitters 如果你添加的监听器超过 10 个就会输出警告信息。 setMaxListeners 函数用于提高监听器的默认限制的数量。

7

listeners(event)
返回指定事件的监听器数组。

8

emit(event, [arg1], [arg2], [...])
按监听器的顺序执行执行每个监听器,如果事件有注册监听返回 true,否则返回 false。

类方法

序号

方法 & 描述

1

listenerCount(emitter, event)
返回指定事件的监听器数量。

events.EventEmitter.listenerCount(emitter, eventName) //已废弃,不推荐

events.emitter.listenerCount(eventName) //推荐

事件:

序号

事件 & 描述

1

newListener

event - 字符串,事件名称

listener - 处理事件函数

该事件在添加新监听器时被触发。

2

removeListener

event - 字符串,事件名称

listener - 处理事件函数

从指定监听器数组中删除一个监听器。需要注意的是,此操作将会改变处于被删监听器之后的那些监听器的索引。

实例

以下实例通过 connection(连接)事件演示了 EventEmitter 类的应用。

var events = require('events');

var eventEmitter = new events.EventEmitter();


// 监听器 #1

var listener1 = function listener1() {

console.log('监听器 listener1 执行。');

}


// 监听器 #2

var listener2 = function listener2() {

console.log('监听器 listener2 执行。');

}


// 绑定 connection 事件,处理函数为 listener1

eventEmitter.addListener('connection', listener1);


// 绑定 connection 事件,处理函数为 listener2

eventEmitter.on('connection', listener2);


var eventListeners = eventEmitter.listenerCount('connection');

console.log(eventListeners + " 个监听器监听连接事件。");


// 处理 connection 事件

eventEmitter.emit('connection');


// 移除监绑定的 listener1 函数

eventEmitter.removeListener('connection', listener1);

console.log("listener1 不再受监听。");


// 触发连接事件

eventEmitter.emit('connection');


eventListeners = eventEmitter.listenerCount('connection');

console.log(eventListeners + " 个监听器监听连接事件。");


console.log("程序执行完毕。");

运行结果:

Error事件

EventEmitter 定义了一个特殊的事件 error,它包含了错误的语义,我们在遇到 异常的时候通常会触发 error 事件。

当 error 被触发时,EventEmitter 规定如果没有响 应的监听器,Node.js 会把它当作异常,退出程序并输出错误信息。

继承eventEmitter

多数时候我们不会直接使用 EventEmitter,而是在对象中继承它。包括 fs、net、 http 在内的,只要是支持事件响应的核心模块都是 EventEmitter 的子类。

为什么要这样做呢?原因有两点:

首先,具有某个实体功能的对象实现事件符合语义, 事件的监听和发生应该是一个对象的方法。

其次 JavaScript 的对象机制是基于原型的,支持 部分多重继承,继承 EventEmitter 不会打乱对象原有的继承关系。



【版权声明】本文为华为云社区用户原创内容,转载时必须标注文章的来源(华为云社区)、文章链接、文章作者等基本信息, 否则作者和本社区有权追究责任。如果您发现本社区中有涉嫌抄袭的内容,欢迎发送邮件进行举报,并提供相关证据,一经查实,本社区将立刻删除涉嫌侵权内容,举报邮箱: cloudbbs@huaweicloud.com
  • 点赞
  • 收藏
  • 关注作者

评论(0

0/1000
抱歉,系统识别当前为高风险访问,暂不支持该操作

全部回复

上滑加载中

设置昵称

在此一键设置昵称,即可参与社区互动!

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。

*长度不超过10个汉字或20个英文字符,设置后3个月内不可修改。