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

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

第一节 Node.js课程介绍

1 查看系统中安装的Node.js版本
2 测试Node.js的控制台输出
3.1 在桌面新建test.js文件
3.2 通过Node.js 交互环境运行文件

笔记

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

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

第二节 Node.js模块

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

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

mymodule.js
index.js
结果

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

module.exports = 函数名;

mymodule.js
index.js
结果

笔记

npm init //创建package.json文件

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)

1.1 新建test.js
1.2 运行代码
1.3 在浏览器输入'127.0.0.1337'每刷新一次会出现一个'shiyanlou'

二、只执行一次的监听器

emitter.once(event,listener)

2.1 新建test2.js
2.1 刷新浏览器,不再出现'shiyanlou'

三、移除监听器

emmiter.removeListener(event,listener)

3.1 新建test3.js
3.2 运行代码
3.3 出现了'shiyanlou',没有出现'Hello World'

四、移除所有监听器

emmiter.removeAllListeners([event])

4.1 新建test4.js
4.2 运行代码
4.3 只出现了'shiyanlou'

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

emitter.setMaxListners(n)

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

六、自定义事件

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

6.1 新建test5.js
6.2 运行及结果

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

EventEmitter.listenerCount(emitter,event)

7.1 新建test6.js
7.2 运行及结果

第四节 Node.js fs模块

一、异步和同步

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

异步实例

fs.unlinkSync(filename); //同步

同步实例

二、readFile读取文件

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

fs.readFile参数

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

2.1 新建text.txt
2.2 新建readfile.js
2.3 运行及结果

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

text.js
运行及结果

三、writeFile写入文件

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

fs.write参数
3.1 新建writeFile.js
3.1.1 运行及结果
3.1.2 运行及结果

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

  • r :read
  • w :write
  • a :append
  • ……
flag:a
运行及结果

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

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

  • mode:文件的权限,默认值0666
  • flags的值

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

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

4.1 fs.read()

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

  • fs.read参数
4.1.1 新建testread.txt
4.1.2 新建read.js
4.1.3 运行及结果

4.2 fs.write()

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

4.2.1 新建write.js
4.2.2.1 运行及结果
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)

  • 只能删除空目录
5.1.1 新建mkdir.js
5.1.2 运行及结果

5.2 读取目录

fs.readdir(path,callback)

5.2.1 新建readdir.js
5.2.2 在newdir文件夹中新建'dir1文件夹'和'file1文件'
5.2.3 运行及结果

第五节 Node.js的http模块

一、创建 http server

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

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

<!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>

五、运行及结果

5.1 运行main.js
5.2.1 地址栏输入127.0.0.1:8080
5.2.2 地址栏输入127.0.0.1:8080/about
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
1.3.1 server.js结果
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
服务器端
客户端-1
客户端-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');
});
运行结果

当无内容输入时,可触发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中运行一个命令,并缓存其输出

新建test_exec.js
运行结果

2.3 child_process.execFile()方法

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

新建test_execfiel.js
运行结果

2.4 child_process.fork()方法

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

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

三、cluster模块

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

3.1 新建 test_cluster.js
3.2 运行及结果

最后编辑于
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 211,290评论 6 491
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 90,107评论 2 385
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 156,872评论 0 347
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 56,415评论 1 283
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 65,453评论 6 385
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 49,784评论 1 290
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 38,927评论 3 406
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 37,691评论 0 266
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 44,137评论 1 303
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 36,472评论 2 326
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 38,622评论 1 340
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 34,289评论 4 329
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 39,887评论 3 312
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 30,741评论 0 21
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 31,977评论 1 265
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 46,316评论 2 360
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 43,490评论 2 348

推荐阅读更多精彩内容

  • https://nodejs.org/api/documentation.html 工具模块 Assert 测试 ...
    KeKeMars阅读 6,312评论 0 6
  • 个人入门学习用笔记、不过多作为参考依据。如有错误欢迎斧正 目录 简书好像不支持锚点、复制搜索(反正也是写给我自己看...
    kirito_song阅读 2,458评论 1 37
  • Node.js是目前非常火热的技术,但是它的诞生经历却很奇特。 众所周知,在Netscape设计出JavaScri...
    w_zhuan阅读 3,609评论 2 41
  • topics: 1.The Node.js philosophy 2.The reactor pattern 3....
    宫若石阅读 1,061评论 0 1
  • 之前用axios在前端用body协议发送,结果在server端获取不到,最后发现原来是dev-server.js里...
    junminghou阅读 3,979评论 0 0