第一节 nodejs课程介绍
一、实验说明:(环境登录、环境介绍、环境使用);
二、nodejs介绍
1.概述
Node.js是基于Chrome JavaScript运行时建立的一个平台,实际上它是对Google Chrome V8引擎进行了封装,它主要用于创建快速的、可扩展的网络应用。Node.js采用事件驱动和非阻塞I/O模型,使其变得轻量和高效,非常适合构建运行在分布式设备的数据密集型的实时应用。
运行于浏览器的JavaScript,浏览器就是JavaScript代码的解析器,而Node.js则是服务器端JavaScript的代码解析器,存在于服务器端的JavaScript代码由Node.js来解析和运行。
JavaScript解析器只是JavaScript代码运行的一种环境,浏览器是JavaScript运行的一种环境,浏览器为JavaScript提供了操作DOM对象和window对象等的接口。Node.js也是JavaScript运行的一种环境,Node.js为JavaScript提供了操作文件、创建HTTP服务、 创建TCP/UDP服务等的接口,所以Node.js可以完成其他后台语言(Python、PHP等)能完成的工作。
2.交互式运行环境
Node.js提供了一个交互式运行环境,通过这个环境,可以立即执行JavaScript代码块,使用方法类似于Chrome浏览器中Firebug插件的Console。
三、nodejs模块和包
1.模块
Node.js官方提供了很多模块,这些模块分别实现了一种功能,如操作文件的模块fs,构建http服务的模块http等,每个模块都是一个JavaScript文件,当然也可以自己编写模块。
2.包
包可以将多个具有依赖关系的模块组织在一起,封装多个模块,以方便管理。Node.js采用了CommonJS规范,根据CommonJS规范规定,一个JavaScript文件就是一个模块,而包是一个文件夹,包内必须包含一个JSON文件,命名为package.json。一般情况下,包内的bin文件夹存放二进制文件,包内的lib文件夹存放JavaScript文件,包内的doc文件夹存放文档,包内的test文件夹存放单元测试。package.json文件中需要包含的字段及包的使用等。
3.npm包管理工具
npm是Node.js的包管理工具,npm定义了包依赖关系标准,我们使用npm主要用来下载第三方包和管理本地下载的第三方包。
第二节 nodejs模块
一、nodejs模块
每一个 Node.js 文件都是一个 Node.js 模块,包括JavaScript文件(.js)、JSON文本文件(.json)和二进制模块文件(.node)。
1.模块的使用
(1)新建一个JS文件,写入内容,就是一个nodejs模块,但是怎么在其他模块中引入并使用这个模块呢?我们需要为模块提供对外的接口,这就要用到module.exports和exports。
(2)在其他模块中,可以使用require(module_name);载入需要的模块。2.moduls.exports和exports
module是一个对象,每个模块中都有一个module对象,module是当前模块的一个引用。module.exports对象是Module系统创建的,而exports可以看作是对module.exports对象的一个引用。在模块中require另一个模块时,以module.exports的值为准,因为有的情况下,module.exports和exports它们的值是不同的。
二、nodejs包
1.包
- 包用于管理多个模块及其依赖关系,可以对多个模块进行封装,包的根目录必须包含package.json文件,package.json文件是CommonJS规范用于描述包的文件,符合CommonJS规范的package.json文件应该包含以下字段:
-
- name:包名。包名是唯一的,只能包含小写字母、数字和下划线。
- version:包版本号。
- description:包说明。
- keywords:关键字数组。用于搜索。
- homepage:项目主页。
- bugs:提交bug的地址。
- license:许可证。
- maintainers:维护者数组。
- contributors:贡献者数组。
- repositories:项目仓库托管地址数组。
- dependencies:包依赖。
2.npm包管理工具
- 由于实验楼环境网络限制,所以npm命令会连接taobao的源,而不会直接连接官方源。 (1)搜索包:npm search *** (2)安装包:npm install *** (3)更新包:npm update *** (4)卸载包:npm uninstall ***
第三节 nodejs events模块
1.简介
在Node.js中,很多对象都会发出事件。比如,fs.readStream打开文件时会发出一个事件。所有发出事件的对象都是events.EventEmitter的实例,可以通过require("events");获得event模块。
通常,事件名采用“驼峰式”命名方式,但是,并没有严格规定。这只是推荐的命名方法。
函数可以添加给对象,对象发出事件时,对应函数就会被执行。这些函数被称作监听器(listeners)。在监听器函数中,this引用的是它(指此监听器函数)添加到的EventEmitter对象。
2.class:events.EventEmitter
通过require('events').EventEmitter得到EventEmitter类。
当EventEmitter对象遇到错误时,通常会触发error事件。error事件在Node.js中是一种特殊情况,如果没有监听器,那么默认会打印出栈跟踪器并退出程序。
3.添加监听器
为事件绑定事件处理程序,可以使用emitter.addListener(event, listener)和emitter.on(event, listener),它们的作用是完全一样的。传入的参数是事件(event)和处理函数(listener)。
4.只执行一次的监听器
使用emitter.once(event, listener)绑定的事件监听器只会执行一次,然后就会被删除掉。
5.移除监听器
移除监听器使用emitter.removeListener(event, listener)。
6.移除所有监听器
移除所有监听器使用emitter.removeAllListeners([event])。
7.设置监听器最大绑定书
emitter.setMaxListeners(n)可以设置同一事件的监听器最大绑定数,默认情况下,超过10个就会警告提示,这能帮我们快速找到类存泄露的地方。显然,不是所有的事件触发器都限制在10个监听器,通过这个方法可以设置,如果设置为0就是无限制。
8.自定义事件
使用emitter.emit(event, [arg1], [arg2], [...])可以触发自定义的事件。
9.查看事件绑定的监听器个数
使用EventEmitter.listenerCount(emitter, event)可以查看事件监听器数量。
第四节 nodejs fs模块
- fs模块用于对系统文件及目标进行读写操作
一、同步和异步
使用require('fs')载入fs模块,模块中所有方法都有同步和异步两种形式。
异步方法中回调函数的第一个参数总是留给异常参数(exception),如果方法成功完成,那么这个参数为null或者undefined。
同步方法执行完并返回结果后,才能执行后续的代码。而异步方法采用回调函数接收返回结果,可以立即执行后续的代码。
fs中unlink\unlinkSync(异步、同步删除文件)方法;
二、readFile读取文件
使用fs.readFile(filename, [options], callback)方法读取文件。
readFile接收三个参数,filename是文件名;[options]是可选的参数,为一个对象,用于指定文件编码(encoding)及操作方式(flag);callback是回调函数。
readFile的回调函数接收两个参数,err是读取文件出错时触发的错误对象,data是从文件读取的数据。
data数据是原始二进制数据在缓冲区中的内容。要显示文件内容可以使用toString()或者设置输出编码(utf-8);
fs.readFileSync(filename, [options])是readFile的同步方法。
三、writeFile写入文件
使用fs.writeFile(filename, data, [options], callback)写入内容到文件。
writeFile接收四个参数,filename是文件名称;data是要写入文件的数据;[options]是一个对象为可选参数,包含编码格式(encoding),模式(mode)以及操作方式(flag);callback是回调函数。
写入文件内容(如果文件不存在会创建一个文件),写入时会先清空文件,还可以传递了追加参数 { 'flag': 'a' }(避免清空);
flag传递的值,r代表读取文件,,w代表写入文件,a代表追加写入文件,还有其他的值不作详细介绍。
四、使用fs.read和fs.write读写文件
使用fs.read和fs.write读写文件需要使用fs.open打开文件和fs.close关闭文件。
1.fs.read()
先介绍fs.open(path, flags, [mode], callback)方法,此方法用于打开文件,以便fs.read()读取。path是文件路径,flags是打开文件的方式,[mode]是文件的权限(可选参数,默认值是0666),callback是回调函数。
- flags的值:
-
- r :读取文件,文件不存在时报错;
- r+ :读取并写入文件,文件不存在时报错;
- rs :以同步方式读取文件,文件不存在时报错;
- rs+ :以同步方式读取并写入文件,文件不存在时报错;
- w :写入文件,文件不存在则创建,存在则清空;
- wx :和w一样,但是文件存在时会报错;
- w+ :读取并写入文件,文件不存在则创建,存在则清空;
- wx+ :和w+一样,但是文件存在时会报错;
- a :以追加方式写入文件,文件不存在则创建;
- ax :和a一样,但是文件存在时会报错;
- a+ :读取并追加写入文件,文件不存在则创建;
- ax+ :和a+一样,但是文件存在时会报错。
fs.close(fd, [callback])用于关闭文件,fd是所打开文件的文件描述符。
fs.read(fd, buffer, offset, length, position, callback)方法接收6个参数。
- fd是文件描述符,必须接收fs.open()方法中的回调函数返回的第二个参数;
- buffer是存放读取到的数据的Buffer对象;
- offset指定向buffer中存放数据的起始位置;
- length指定读取文件中数据的字节数;
- position指定在文件中读取文件内容的起始位置;
- callback是回调函数,回调函数的参数:
-
- err用于抛出异常;
- bytesRead是从文件中读取内容的实际字节数;
- buffer是被读取的缓存区对象。
var fs = require('fs'); // 引入fs模块// 打开文件fs.open('./testread.txt', 'r', function(err, fd) { if (err) { throw err; } console.log('open file success.'); var buffer = new Buffer(255); // 读取文件 fs.read(fd, buffer, 0, 10, 0, function(err, bytesRead, buffer) { if (err) { throw err; } // 打印出buffer中存入的数据 console.log(bytesRead, buffer.slice(0, bytesRead).toString()); // 关闭文件 fs.close(fd); });});
fs.write()
fs.write(fd, buffer, offset, length, position, callback)方法的参数和fs.read()相同,buffer是需要写入文件的内容。
五、目录操作
1.创建目录
使用fs.mkdir(path, [mode], callback)创建目录,path是需要创建的目录,[mode]是目录的权限(默认值是0777),callback 是回调函数。
删除目录可以使用fs.rmdir(path, callback),但是只能删除空目录。
2.读取目录
使用fs.readdir(path, callback)读取文件目录。
var fs = require('fs'); // 引入fs模块fs.readdir('./newdir', function(err, files) { if (err) { throw err; } // files是一个数组 // 每个元素是此目录下的文件或文件夹的名称 console.log(files);});
第五节 nodejs的http模块
- http模块主要用于创建http server服务,还会讲到url模块和path模块,同时也会用到前面讲过的fs模块。url模块用于解析url,path模块用于处理和转换文件路径。
一、创建http server(起一个web服务器,监听请求)
二、创建路由(根据不同的请求,调用不同的handle<处理函数>)
三、创建主程序(把router、handler作为参数启动web服务器,做到低耦合)
四、创建HTML文件(根据不同的请求,显示不同的内容)
第六节 nodejs中的网络编程
- 主要讲解TCP和UDP的网络编程,net模块提供了一个异步网络包装器,用于TCP网络编程,它包含了创建服务器和客户端的方法。dgram模块用于UDP网络编程。
TCP Server
- net模块通过net.createServer方法创建TCP服务器,通过net.connect方法创建客户端去连接服务器。
- process是一个Node.js的全局模块,可以在任何地方直接使用而无需通过require方法引入。process模块允许你获得或者修改当前Node.js进程的设置。process.stdin用于获取来自标准输入的可读流(Readable Stream)。
- 监听服务消息有:data,end,connection,error,close等;
UDP Server
- UDP和TCP类似,通过dgram.createSocket创建服务。
- 发送的消息必须通过Buffer创建。
- 监听服务消息有:error,message,listening等;
第七节 nodejs中的进程
- 主要学习Node.js中的进程,涉及的模块是Cluster、Porcess和child_process。
process模块
process使用时直接通过全局变量process访问,而不需要通过require方法加载。process用来和当前Node.js程序进程互动。
process是EventEmitter的一个实例,process主要包含退出事件、信号事件以及一些属性。
退出事件(exit)
当退出当前进程时,会促发exit事件,exit事件的回调函数中只接受同步操作,并且回调函数只接受一个参数,即退出代码,如:
process.on('exit', function(code) { setTimeout(function() { console.log('This will not run'); }, 0); console.log('exit code: ', code);});
运行上面的代码,其中setTimeout方法中的回调函数是不会被执行的,因为exit事件中只会运行同步操作,而不会运行异步操作。
在exit事件之前还有一个beforeExit事件会被触发,在beforeExit的回调函数中可以执行异步操作。值得注意的是,通过process.exit()退出程序或者因为发生错误而退出程序是不会触发beforeExit事件的。顺便说一下,当有错误未被捕获时,就会触发uncaughtException事件。
信号事件
信号事件就是接收到某个特定信号才会被触发的事件。
比如SIGINT事件的触发方式是ctrl+c:
process.stdin.resume();process.on('SIGINT', function() { console.log('Got SIGINT. Press Control-D to exit.');});
运行代码,然后按住control键,再按C键就会触发SIGINT事件。
属性
process模块提供了很多属性,其中关于IO输入输出的主要有三个:
- process.stdin // 标准输入
- process.stdout // 标准输出
- process.stderr // 标准错误
例子:// stdin.jsprocess.stdin.setEncoding('utf8');process.stdin.on('readable', function() { var chunk = process.stdin.read(); if (chunk !== null) { process.stdout.write('data: ' + chunk); }});process.stdin.on('end', function() { process.stdout.write('end');});
运行,输入任意字符,Node.js会把输入的字符打印出来,输入ctrl+D触发end事件。
还有其他属性,比如process.argv是一个包含了命令行参数的数组。
方法
process模块还有很多实用的方法,比如:
- process.cwd() // 返回脚本运行工作目录
- process.chdir() // 切换工作目录
- process.exit() // 退出当前进程
- process.on() // 添加监听事件 //...
child_process模块
child_process用于创建子进程。
child_process.spawn()方法
通过当前命令启动一个新的进程。如:
// test_spawn.jsvar spawn = require('child_process').spawn, ls = spawn('ls', ['-lh', '/usr']);ls.stdout.on('data', function (data) { console.log('stdout: ' + data);});ls.stderr.on('data', function (data) { console.log('stderr: ' + data);});ls.on('close', function (code) { console.log('child process exited with code ' + code);});
运行,从结果可以看出,子进程成功运行了ls -lh /usr命令。
child_process.exec()方法
child_process.execFile()方法
- 与exec方法类似,执行特定程序文件,参数通过一个数组传送。
child_process.fork()方法
直接创建一个子进程,此进程是node命令的子进程,fork('./sub.js')相当于spwan('node', './sub.js')。fork还会在父进程与子进程之间,建立一个通信管道,通过child.send()发送消息。
cluster模块
单个的Node实例运行在单个线程中。要发挥多核系统的能力,需要启动一个Node进程集群来处理负载。cluster模块就用于创建共享服务器端口的子进程。
// test_cluster.jsvar cluster = require('cluster');var http = require('http');var numCPUs = require('os').cpus().length; // 获取CPU内核数// master是主进程// 此处判断是否为master进程// 是则根据CPU内核数创建worker进程if (cluster.isMaster) { // worker是运行节点 // 根据CPU数量启动worker // Fork workers for (var i = 0; i < numCPUs; i++) { cluster.fork(); } Object.keys(cluster.workers).forEach(function(id) { console.log('I am running with ID : ' + cluster.workers[id].process.pid); }); cluster.on('exit', function(worker, code, signal) { console.log('worker ' + worker.process.pid + ' died'); });} else { // cluster.isWorker == true // 运行到else中的代码 // 说明当前进程是worker进程 // 那么此worker进程就启动一个http服务 http.createServer(function(req, res) { res.writeHead(200); res.end("hello world\n"); }).listen(8000);}
运行程序
I am running with ID : 38576I am running with ID : 38600I am running with ID : 38608I am running with ID : 38640
在终端会看到根据CPU内核数创建的子进程信息。
每个worker进程都是通过child_process.fork()方法产生的,所以它们可以通过IPC(进程间通信)与master进程通信。