Node.js 教程|实验代码+运行结果

课程来源:https://www.shiyanlou.com/courses/44【@实验楼】

第一节 Node.js课程介绍

1 查看系统中安装的Node.js版本
2 测试Node.js的控制台输出
Node.js 教程|实验代码+运行结果_第1张图片
3.1 在桌面新建test.js文件
3.2 通过Node.js 交互环境运行文件

笔记

一个js文件就是一个模块,而包是一个文件夹,包内必须包含一个JSON文件,命名为package.json。

  • bin文件夹:二进制文件
  • lib文件夹:js文件
  • doc文件夹:文档
  • test文件夹:单元测试
  • package.json

第二节 Node.js模块

一、暴露一个文件里的多个函数

exports.引用名(自定义) = 函数名;
exports.引用名(自定义) = 函数名;
……

Node.js 教程|实验代码+运行结果_第2张图片
mymodule.js
index.js
结果

二、暴露一个文件里的一个函数

module.exports = 函数名;

Node.js 教程|实验代码+运行结果_第3张图片
mymodule.js
index.js
结果

笔记

npm init //创建package.json文件

Node.js 教程|实验代码+运行结果_第4张图片
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)

Node.js 教程|实验代码+运行结果_第5张图片
1.1 新建test.js
1.2 运行代码
1.3 在浏览器输入'127.0.0.1337'每刷新一次会出现一个'shiyanlou'

二、只执行一次的监听器

emitter.once(event,listener)

Node.js 教程|实验代码+运行结果_第6张图片
2.1 新建test2.js
2.1 刷新浏览器,不再出现'shiyanlou'

三、移除监听器

emmiter.removeListener(event,listener)

Node.js 教程|实验代码+运行结果_第7张图片
3.1 新建test3.js
3.2 运行代码
3.3 出现了'shiyanlou',没有出现'Hello World'

四、移除所有监听器

emmiter.removeAllListeners([event])

Node.js 教程|实验代码+运行结果_第8张图片
4.1 新建test4.js
4.2 运行代码
Node.js 教程|实验代码+运行结果_第9张图片
4.3 只出现了'shiyanlou'

五、设置监听器最大绑定数

emitter.setMaxListners(n)

  • 默认情况,超过10个就会警告提示
  • n = 0,无限制

六、自定义事件

emitter.emit(event,[arg1],[arg2],[...])

Node.js 教程|实验代码+运行结果_第10张图片
6.1 新建test5.js
6.2 运行及结果

七、查看事件绑定的监听器个数

EventEmitter.listenerCount(emitter,event)

Node.js 教程|实验代码+运行结果_第11张图片
7.1 新建test6.js
Node.js 教程|实验代码+运行结果_第12张图片
7.2 运行及结果

第四节 Node.js fs模块

一、异步和同步

fs.unlink(filename,callback); //异步

Node.js 教程|实验代码+运行结果_第13张图片
异步实例

fs.unlinkSync(filename); //同步

Node.js 教程|实验代码+运行结果_第14张图片
同步实例

二、readFile读取文件

fs.readFile(filename,[options],callback)

Node.js 教程|实验代码+运行结果_第15张图片
fs.readFile参数

原始二进制数据在缓冲区的内容

2.1 新建text.txt
Node.js 教程|实验代码+运行结果_第16张图片
2.2 新建readfile.js
2.3 运行及结果

使用toString()或者设置输出编码,修改readFile.js

Node.js 教程|实验代码+运行结果_第17张图片
text.js
Node.js 教程|实验代码+运行结果_第18张图片
运行及结果

三、writeFile写入文件

fs.writeFile(filename,data,[options],callback)

Node.js 教程|实验代码+运行结果_第19张图片
fs.write参数
Node.js 教程|实验代码+运行结果_第20张图片
3.1 新建writeFile.js
3.1.1 运行及结果
Node.js 教程|实验代码+运行结果_第21张图片
3.1.2 运行及结果

如果要追加数据到文件,可以传递一个flag参数

  • r :read
  • w :write
  • a :append
  • ……
Node.js 教程|实验代码+运行结果_第22张图片
flag:a
运行及结果

四、使用fs.read和fs.write读写文件

fs.open(path,flags,[mode],callback) 打开文件

  • mode:文件的权限,默认值0666
  • Node.js 教程|实验代码+运行结果_第23张图片
    flags的值

fs.close(fd,[callback]) 关闭文件

  • fd:所打开文件的文件描述符

4.1 fs.read()

fs.read(fd,buffer,offset,length,position,callback)

  • Node.js 教程|实验代码+运行结果_第24张图片
    fs.read参数
Node.js 教程|实验代码+运行结果_第25张图片
4.1.1 新建testread.txt
Node.js 教程|实验代码+运行结果_第26张图片
4.1.2 新建read.js
4.1.3 运行及结果

4.2 fs.write()

fs.write(fd,buffer,offset,length,position,callback)

Node.js 教程|实验代码+运行结果_第27张图片
4.2.1 新建write.js
4.2.2.1 运行及结果
Node.js 教程|实验代码+运行结果_第28张图片
4.2.2.2 运行及结果

遇到的问题

在实验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)

  • 只能删除空目录
Node.js 教程|实验代码+运行结果_第29张图片
5.1.1 新建mkdir.js
Node.js 教程|实验代码+运行结果_第30张图片
5.1.2 运行及结果

5.2 读取目录

fs.readdir(path,callback)

Node.js 教程|实验代码+运行结果_第31张图片
5.2.1 新建readdir.js
Node.js 教程|实验代码+运行结果_第32张图片
5.2.2 在newdir文件夹中新建'dir1文件夹'和'file1文件'
5.2.3 运行及结果

第五节 Node.js的http模块

一、创建 http server

1.1 通过Node.js创建(简单方式)

Node.js 教程|实验代码+运行结果_第33张图片
1.1.1 新建 demo.js
1.1.2 运行及结果

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



    
        
        Home page
    
    
        

home page

4.2 about.html



    
        
        About page
    
    
        

about page

4.3 404.html



    
        
        404 page
    
    
        

404 page not found

五、运行及结果

5.1 运行main.js
Node.js 教程|实验代码+运行结果_第34张图片
5.2.1 地址栏输入127.0.0.1:8080
Node.js 教程|实验代码+运行结果_第35张图片
5.2.2 地址栏输入127.0.0.1:8080/about
Node.js 教程|实验代码+运行结果_第36张图片
5.2.3 地址栏输入127.0.0.1:8080/others

第六节 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
Node.js 教程|实验代码+运行结果_第37张图片
1.3.1 server.js结果
Node.js 教程|实验代码+运行结果_第38张图片
1.3.2 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
Node.js 教程|实验代码+运行结果_第39张图片
服务器端
Node.js 教程|实验代码+运行结果_第40张图片
客户端-1
Node.js 教程|实验代码+运行结果_第41张图片
客户端-2

遇到的问题

从运行结果截图可以看出,能正常聊天,但是输入‘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.');
});
先运行,按Ctrl-C触发‘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');
});
Node.js 教程|实验代码+运行结果_第42张图片
运行结果

当无内容输入时,可触发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');
});
Node.js 教程|实验代码+运行结果_第43张图片
运行结果

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);
});
Node.js 教程|实验代码+运行结果_第44张图片
运行结果

2.2 child_process.exec()方法

在shell中运行一个命令,并缓存其输出

Node.js 教程|实验代码+运行结果_第45张图片
新建test_exec.js
运行结果

2.3 child_process.execFile()方法

与exec方法类似,执行特定程序文件,参数通过一个数组传递

Node.js 教程|实验代码+运行结果_第46张图片
新建test_execfiel.js
Node.js 教程|实验代码+运行结果_第47张图片
运行结果

2.4 child_process.fork()方法

  • fork('./sub.js')相当于spwan('node','./sub.js')
  • fork还会在父进程和子进程之间,建立一个通信管道,通过child.send()发送消息
Node.js 教程|实验代码+运行结果_第48张图片
2.4.1 新建main.js
2.4.2 新建sub.js
2.4.3 运行及结果
遇到的问题
Node.js 教程|实验代码+运行结果_第49张图片
在windows运行test_spawn.js
错误原因

所以该模块实验选择了实验楼配置的Linux环境

三、cluster模块

单个的Node实例运行在单个线程中。要发挥多核系统的能力,需要启动一个Node进程集群来处理负载。cluster模块就用于创建共享服务器端口的子进程。

Node.js 教程|实验代码+运行结果_第50张图片
3.1 新建 test_cluster.js
3.2 运行及结果

你可能感兴趣的:(Node.js 教程|实验代码+运行结果)