Node.js的核心模块服务器API
node的核心模块:
node为JavaScript提供了很多服务器级别的API,这些API绝大多数都被包装到了一个具名的核心模块中。
例如:
1. 文件操作的fs核心模块
2. http服务构建的http模块
3. path路径操作模块
4. os操作系统信息模块
等等。。。
那么,以后只要是我们说这个模块是一个核心模块,想要使用它的话,就必须引入模块
var fs = require(“fs”); |
例如:我们做个获取系统cpu信息的例子
//引入模块 |
自定义模块:
这时候,我们来新建一个文件夹,创建一个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的消息等等,那么这些数据在服务器中如何找到相对应的软件进行接收了?这就使用的每个软件都会有一个自己的端口,端口号。
那么端口号用来定义具体的应用程序
那么我们又有一个问题了,那就是服务怎样获取客户端的端口号?
/* |
端口号的范围是:0--65536之间
在计算机中有一些默认的端口号,我们最好不要去使用
如:http服务的80
那么我们在开发的过程之中最好使用一些好记的如:3000,5000,4000等
可以同时开启多个服务,但是一定要确保不同的服务占用的端口号不一致才行
响应内容的类型:
例如我们响应的数据是中文的时候,我们会发现它会乱码,那么我们就需要了解一下
在服务端默认发送的数据,其实是UTF8编码的内容,
而浏览器在不知道服务器响应编码的情况下会按照当前操作系统的默认编码去解析,那么中文操作系统的默认编码是GBK,
解决方法就是正常的告诉浏览器我给你发送的内容是什么编码
也就是解决响应的中文乱码的问题
|
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 提供了多个属性,如 on 和 emit。on 函数用于绑定事件函数,emit 属性用于触发一个事件。接下来我们来具体看下 EventEmitter 的属性介绍。
方法:
序号 |
方法 & 描述 |
1 |
addListener(event, listener) |
2 |
on(event, listener) 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) |
7 |
listeners(event) |
8 |
emit(event, [arg1], [arg2], [...]) |
类方法
序号 |
方法 & 描述 |
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 不会打乱对象原有的继承关系。
- 点赞
- 收藏
- 关注作者
评论(0)