课程来源:https://www.shiyanlou.com/courses/44【@实验楼】
第一节 Node.js课程介绍
笔记
一个js文件就是一个模块,而包是一个文件夹,包内必须包含一个JSON文件,命名为package.json。
- bin文件夹:二进制文件
- lib文件夹:js文件
- doc文件夹:文档
- test文件夹:单元测试
- package.json
第二节 Node.js模块
一、暴露一个文件里的多个函数
exports.引用名(自定义) = 函数名;
exports.引用名(自定义) = 函数名;
……
二、暴露一个文件里的一个函数
module.exports = 函数名;
笔记
npm init //创建package.json文件
npm search express //搜索express包
npm install -g express //安装包,-g即global全局安装
npm updata express //更新包
npm uninstall express //卸载包
第三节 Node.js Events模块
一、添加监听器
emitter.addListener(event,listener)
emitter.on(event,listener)
二、只执行一次的监听器
emitter.once(event,listener)
三、移除监听器
emmiter.removeListener(event,listener)
四、移除所有监听器
emmiter.removeAllListeners([event])
五、设置监听器最大绑定数
emitter.setMaxListners(n)
- 默认情况,超过10个就会警告提示
- n = 0,无限制
六、自定义事件
emitter.emit(event,[arg1],[arg2],[...])
七、查看事件绑定的监听器个数
EventEmitter.listenerCount(emitter,event)
第四节 Node.js fs模块
一、异步和同步
fs.unlink(filename,callback); //异步
fs.unlinkSync(filename); //同步
二、readFile读取文件
fs.readFile(filename,[options],callback)
原始二进制数据在缓冲区的内容
使用toString()或者设置输出编码,修改readFile.js
三、writeFile写入文件
fs.writeFile(filename,data,[options],callback)
如果要追加数据到文件,可以传递一个flag参数
- r :read
- w :write
- a :append
- ……
四、使用fs.read和fs.write读写文件
fs.open(path,flags,[mode],callback) 打开文件
- mode:文件的权限,默认值0666
fs.close(fd,[callback]) 关闭文件
- fd:所打开文件的文件描述符
4.1 fs.read()
fs.read(fd,buffer,offset,length,position,callback)
4.2 fs.write()
fs.write(fd,buffer,offset,length,position,callback)
遇到的问题
在实验4.2中
文档中的实验代码是fs.write(fd,buffer,0,6,0……),实际上应该只写入了‘shiyan’
但是,如上所述的read代码,无论写入多少位内容,都会输出‘shiyanlou’,所以为了保持一致,我将源码进行了修改。
五、目录操作
5.1 创建目录
fs.mkdir(path,[mode],callback)
- path:需要创建的目录
- mode:目录的权限
fs.rmdir(path,mkdir)
- 只能删除空目录
5.2 读取目录
fs.readdir(path,callback)
第五节 Node.js的http模块
一、创建 http server
1.1 通过Node.js创建(简单方式)
1.2 复杂方式
新建app文件夹,在app文件夹中新建server.js,代码如下:
/*
创建 http server
*/
// 加载所需模块
var http = require('http');
var url = require('url');
var fs = require('fs');
// 设置ip和端口
// 实际应用中,可以把这些写到配置文件中
var host = '127.0.0.1',
port = 8080;
// 创建http server
function start(route, handle) {
// 参数
// route 判断url是否存在,存在则调用handle处理,不存在则返回404
// handle 处理不同的url请求
// 处理request请求
function onRequest(req, res) {
// 使用url.parse()方法解析url
// 它会把url string转化为一个object
// 这样我们就可以很方便的获取url中的host、port、pathname等值了
var pathname = url.parse(req.url).pathname;
console.log('Request for ' + pathname + ' received.');
// 判断并处理不同url请求
// 后面介绍此方法
route(handle, pathname, res, req);
}
// 使用http.createSserver()方法创建http server
// 并传入onRequest()方法
// 然后使用listen()方法监听指定地址
http.createServer(onRequest).listen(port, host);
console.log('Server has started and listening on ' + host + ':' + port);
}
// 导出 start 方法
exports.start = start;
二、创建路由
2.1 在app文件夹中新建router.js,代码如下:
var fs = require('fs');
// 路由函数
// 处理不同url的请求
// 并返回相应内容
function route(handle, pathname, res, req) {
console.log('About to route a request for ' + pathname);
// 判断url是否存在特定处理函数
// 存在则调用handle处理
// 不存在则返回404页面
if (typeof handle[pathname] === 'function') {
// handle用于处理不同的url请求
handle[pathname](res, req);
} else {
console.log('No request handler found for ' + pathname);
// 读取404页面
// 所有页面都存放在view文件夹下
var content = fs.readFileSync('./views/404.html');
res.writeHead(404, {'Content-Type': 'text/html'});
res.write(content);
res.end();
}
}
// 导出route方法
exports.route = route;
2.2 在app文件夹中新建requestHandlers.js文件,代码如下:
// 处理url请求
// 读取文件,输出到response
var fs = require('fs');
// home.html主页
function home(res) {
console.log('Request handler "home" was called.');
// 读取home.html文件
var content = fs.readFileSync('./views/home.html');
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(content);
res.end();
}
// about.html关于页面
function about(res) {
console.log('Request handler "about" was called.');
// 读取about.html文件
var content = fs.readFileSync('./views/about.html');
res.writeHead(200, {'Content-Type': 'text/html'});
res.write(content);
res.end();
}
// 导出页面处理函数
exports.home = home;
exports.about = about;
三、创建主程序
3.1 在app文件夹新建main.js文件,代码如下:
// 主程序
// 引入server,router及requestHandler
var server = require('./server');
var router = require('./router');
var requestHandlers = require('./requestHandlers');
// 保存url处理方法
var handle = {};
handle['/'] = requestHandlers.home;
handle['/about'] = requestHandlers.about;
// 启动http server
server.start(router.route, handle);
四、创建HTML文件
在app文件夹中新建views文件夹,在views文件夹中,新建home.html文件、about.html文件和404.html文件。
4.1 home.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>Home page</title>
</head>
<body>
<p>home page</p>
</body>
</html>
4.2 about.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>About page</title>
</head>
<body>
<p>about page</p>
</body>
</html>
4.3 404.html
<!DOCTYPE html>
<html>
<head>
<meta charset="UTF-8">
<title>404 page</title>
</head>
<body>
<p>404 page not found</p>
</body>
</html>
五、运行及结果
第六节 Node.js中的网络编程
一、TCP Server
net模块通过net.createServer方法创建TCP服务器,通过net.connect方法创建客户端去连接服务器。
1.1 新建server.js
var net = require('net');
// 创建TCP服务器
var server = net.createServer(function (socket) {
console.log('client connected');
// 监听客户端的数据
socket.on('data', function (data) {
console.log('server got datta from client:', data.toString());
});
// 监听客户端断开连接事件
socket.on('end', function (data) {
console.log('connection closed');
});
// 发送数据给客户端
socket.write('Hello\r\n');
});
// 启动服务
server.listen(8080, function () {
console.log('server bound');
});
1.2 新建 client.js
var net = require('net');
// 连接服务器
var client = net.connect({port: 8080}, function () {
console.log('connected to server');
client.write('World!\r\n');
});
// 接收服务端的数据
client.on('data', function (data) {
console.log('client got data from server:', data.toString());
// 断开连接
client.end();
});
// 断开连接
client.on('end', function () {
console.log('disconneected from server');
});
1.3 运行及结果
- 先在一个终端 运行server.js
- 再在另一个终端 运行client.js
二、简易聊天室
2.1 服务端——新建chatServer.js,代码如下:
var net = require('net');
// 创建TCP服务器
var server = net.createServer();
// 存储所有客户端socket
var sockets = [];
// 接受客户端连接请求
server.on('connection', function (socket) {
console.log('Got a new connection');
// 接收所有的用户连接(因为是聊天室,允许多个客户端用户同时连接)
sockets.push(socket);
// 获取客户端发送过来的数据
socket.on('data', function (data) {
console.log('Got data:', data.toString());
// 服务器广播数据,把来自客户端的数据转发送给其他所有客户端
sockets.forEach(function (otherSocket) {
if (otherSocket !== socket) {
otherSocket.write(data);
}
});
});
// 把需要关闭连接的客户端从服务器广播列表中给删除掉
socket.on('close', function () {
console.log('A client connection closed');
var index = sockets.indexOf(socket);
sockets.splice(index, 1);
});
});
server.on('error', function (err) {
console.log('Server error:', err.message);
});
server.on('close', function () {
console.log('Server closed');
});
server.listen(8080);
2.2 客户端,新建chatClient.js,代码如下:
var net = require('net');
process.stdin.resume();
process.stdin.setEncoding('utf8');
var client = net.connect({port: 8080}, function () {
console.log('Connected to server');
// 获取输入的字符串
console.log('input:');
process.stdin.on('data', function (data) {
// 发送输入的字符串到服务器
console.log('input:');
client.write(data);
// 输入'close'字符串时关闭连接
if (data == 'close\n\n') {
client.end();
}
});
});
// 获取服务器端发送过来的数据
client.on('data', function (data) {
console.log('Other user\'s input', data.toString());
});
client.on('end', function () {
console.log('Disconnected from server');
// 退出客户端程序
process.exit();
});
2.3 运行及结果
- 先在一个终端 运行chatServer.js
- 再在另一个终端 运行chatClient.js
- 再在再在另一个终端 运行chatClient.js
遇到的问题
从运行结果截图可以看出,能正常聊天,但是输入‘close’时却无法正常退出!
三、UDP Server
3.1 服务端——新建udpServer.js,代码如下:
var dgram = require('dgram');
var server = dgram.createSocket('udp4');
server.on('error', function (err) {
console.log('serevr error:\n' + err.stack);
server.close();
});
// 接收来自客户端的消息
server.on('message', function (msg, rinfo) {
console.log('server got:' + msg.toString() + 'from' + rinfo.port);
});
// 监听服务
server.on('listening',function(){
var address = server.address();
console.log('server listening on '+address.address+':'+address.port);
});
server.bind(41234);
// server listening 0.0.0..0:41224
3.2 客户端——新建udpClient.js,代码如下:
var dgram = require('dgram');
var client = dgram.createSocket('udp4');
// 发送的消息必须通过Buffer创建
var message = new Buffer('hello shiyanlou!\n');
client.send(message, 0, message.length, 41234, 'localhost', function (err, bytes) {
client.close();
});
3.3 运行及结果
- 先在一个终端 运行updServer.js
- 再在另一个终端 运行udpClient.js
笔记
- 作为web服务器软件,Node.js提供了net模块用于tcp通信,dgram模块用于udp通信
- net.createServer()用于创建tcp监听服务器,net.connect()用于客户端连接tcp服务器
- dgram模块通过dgram.createSocket()创建udp socket,通过bind()监听特定端口,通过send()向特定socket发送信息
第七节 Node.js中的进程
- process模块用于提供和程序主程序有关的功能
- child_process用于创建子程序
- cluster用于处理集群相关编程
一、process模块
1.1 退出事件(exit)
- exit事件的回调函数中接收同步操作,并且回调函数只接受一个参数
- 在exit事件之前还有一个beforeExit事件会被触发,在beforeExit的回调函数中可以异步操作
- 通过process.exit()退出程序或者因为发生错误而退出程序是不会触发beforeExit事件的
- 当有错误未被捕获时,就会触发uncauhgtException事件
新建try-exit.js,代码如下:
process.on('exit', function (code) {
setTimeout(function () {
console.log('This will not run');
}, 0);
console.log('exit code', code);
});
上述代码中,setTimeout方法中的回调函数不会被执行
1.2 信号事件
信号事件就是接受到某个特定信号才会被触发的事件。
新建sigint.js,代码如下:
process.stdin.resume();
process.on('SIGINT', function () {
console.log('Got SIGINT.');
});
1.3 属性
IO 输入输出主要有三个
- process.stdin // 标准输入
- process.stdout // 标准输出
- process.stderr // 标准错误
遇到的问题
新建stdin.js,源代码如下:
process.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');
});
当无内容输入时,可触发end事件,但结果里按回车没有触发,因为'\n'算两个字符,所以修改代码如下:
process.stdin.setEncoding('utf8');
process.stdin.on('readable', () => {
var chunk = process.stdin.read();
// 新增的代码
if (typeof chunk === 'string') {
chunk = chunk.slice(0, -2);
process.stdout.write(`stringLength:${chunk.length}\n`);
}
if (chunk === '') {
process.stdin.emit('end');
return;
}
if (chunk !== null) {
process.stdout.write(`data: ${chunk}\n`);
}
});
process.stdin.on('end', () => {
process.stdout.write('end');
});
1.4 方法
- process.cwd() //返回脚本运行工作目录
- process.chdir() //切换工作目录
- process.exit() //退出当前进程
- process.on() //添加监听事件
- ...
二、child_process模块
用于创建子进程
2.1 child_process.spawn()方法
新建test_spawn.js,代码如下:
var 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);
});
2.2 child_process.exec()方法
在shell中运行一个命令,并缓存其输出
2.3 child_process.execFile()方法
与exec方法类似,执行特定程序文件,参数通过一个数组传递
2.4 child_process.fork()方法
- fork('./sub.js')相当于spwan('node','./sub.js')
- fork还会在父进程和子进程之间,建立一个通信管道,通过child.send()发送消息
遇到的问题
所以该模块实验选择了实验楼配置的Linux环境
三、cluster模块
单个的Node实例运行在单个线程中。要发挥多核系统的能力,需要启动一个Node进程集群来处理负载。cluster模块就用于创建共享服务器端口的子进程。