|
|
|
@ -0,0 +1,327 @@
|
|
|
|
|
# Node.js学习
|
|
|
|
|
|
|
|
|
|
## nodejs核心api
|
|
|
|
|
|
|
|
|
|
### 核心api:
|
|
|
|
|
|
|
|
|
|
#### Buffer
|
|
|
|
|
|
|
|
|
|
JavaScript 语言没有用于读取或操作二进制数据流的机制。 `Buffer` 类是作为 Node.js API 的一部分引入的,用于在 TCP 流、文件系统操作、以及其他上下文中与八位字节流进行交互。(js无法给数组固定长度进行操作,nodejs中引入Buffer这个api方便操作数组)
|
|
|
|
|
|
|
|
|
|
#### dgram
|
|
|
|
|
|
|
|
|
|
`dgram` 模块提供了 UDP 数据包 socket 的实现。
|
|
|
|
|
|
|
|
|
|
#### Events事件触发器
|
|
|
|
|
|
|
|
|
|
Node.js 核心 API 构建于惯用的异步事件驱动架构,其中某些类型的对象(又称触发器,Emitter)会触发命名事件来调用函数(又称监听器,Listener)。
|
|
|
|
|
|
|
|
|
|
例如,[`net.Server`](http://nodejs.cn/s/gBYjux) 会在每次有新连接时触发事件,[`fs.ReadStream`](http://nodejs.cn/s/C3Eioq) 会在打开文件时触发事件,[stream](http://nodejs.cn/s/kUvpNm)会在数据可读时触发事件。
|
|
|
|
|
|
|
|
|
|
所有能触发事件的对象都是 `EventEmitter` 类的实例。 这些对象有一个 `eventEmitter.on()` 函数,用于将一个或多个函数绑定到命名事件上。 事件的命名通常是驼峰式的字符串,但也可以使用任何有效的 JavaScript 属性键。。
|
|
|
|
|
|
|
|
|
|
当 `EventEmitter` 对象触发一个事件时,所有绑定在该事件上的函数都会被同步地调用。 被调用的监听器返回的任何值都将会被忽略并丢弃。
|
|
|
|
|
|
|
|
|
|
例子,一个简单的 `EventEmitter` 实例,绑定了一个监听器。 `eventEmitter.on()` 用于注册监听器, `eventEmitter.emit()` 用于触发事件。
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const EventEmitter = require('events');
|
|
|
|
|
|
|
|
|
|
class MyEmitter extends EventEmitter {}
|
|
|
|
|
|
|
|
|
|
const myEmitter = new MyEmitter();
|
|
|
|
|
|
|
|
|
|
myEmitter.on('event', () => {
|
|
|
|
|
|
|
|
|
|
console.log('触发事件');
|
|
|
|
|
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
myEmitter.emit('event');
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
`eventEmitter.emit()` 方法可以传任意数量的参数到监听器函数。 当监听器函数被调用时, `this` 关键词会被指向监听器所绑定的 `EventEmitter` 实例。
|
|
|
|
|
|
|
|
|
|
#### fs模块
|
|
|
|
|
|
|
|
|
|
在 NodeJS 中,所有与文件操作都是通过 `fs` 核心模块来实现的,包括文件目录的创建、删除、查询以及文件的读取和写入,在 `fs` 模块中,所有的方法都分为同步和异步两种实现,具有 `sync` 后缀的方法为同步方法,不具有 `sync` 后缀的方法为异步方法,文件的前置知识,如文件的权限位 `mode` 、标识位 `flag` 、文件描述符 `fd` 等。
|
|
|
|
|
|
|
|
|
|
因为 `fs` 模块需要对文件进行操作,会涉及到操作权限的问题,所以需要先清楚文件权限是什么,都有哪些权限。
|
|
|
|
|
|
|
|
|
|
#### http模块
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
var http = require('http');
|
|
|
|
|
http.createServer(function (req, res) {
|
|
|
|
|
res.writeHead(200, { 'Content-Type': 'text/html' });
|
|
|
|
|
res.write('<h1>Node.js</h1>');
|
|
|
|
|
res.end('<p>Hello World</p>');
|
|
|
|
|
}).listen(3000);
|
|
|
|
|
console.log("HTTP server is listening at port 3000.");
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
#### http2模块
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const http2 = require('http2');
|
|
|
|
|
const fs = require('fs');
|
|
|
|
|
|
|
|
|
|
const server = http2.createSecureServer({
|
|
|
|
|
key: fs.readFileSync('rsa_private.key'),
|
|
|
|
|
cert: fs.readFileSync('cert.crt')
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
server.on('error', (err) => console.error(err));
|
|
|
|
|
|
|
|
|
|
server.on('stream', (stream, headers) => {
|
|
|
|
|
// stream is a Duplex
|
|
|
|
|
stream.respond({
|
|
|
|
|
'content-type': 'text/html',
|
|
|
|
|
':status': 200
|
|
|
|
|
});
|
|
|
|
|
stream.end('<h1>Hello World</h1>');
|
|
|
|
|
});
|
|
|
|
|
|
|
|
|
|
server.listen(8443);
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
## Nodejs网络通信
|
|
|
|
|
|
|
|
|
|
### 构建TCP服务
|
|
|
|
|
|
|
|
|
|
新建server.js文件,创建server服务
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const net = require('net')
|
|
|
|
|
// 使用net的createServer方法创建服务
|
|
|
|
|
const server = net.createServer()
|
|
|
|
|
// 声明一个数组储存多个客户端socket
|
|
|
|
|
const clients = []
|
|
|
|
|
// 使用服务的on方法监听服务器信息
|
|
|
|
|
server.on('connection', clientSocket的on方法监听传入的数据(二进制) => {
|
|
|
|
|
// 获取多个客户的存入clients
|
|
|
|
|
clients.push(clientSocket)
|
|
|
|
|
// 使用clientSocket的on方法监听传入的数据(二进制)
|
|
|
|
|
clientSocket.on('data', data => {
|
|
|
|
|
console.log(data.toString());
|
|
|
|
|
// 给多个客户端发送数据除发送者外
|
|
|
|
|
clients.forEach(socket => {
|
|
|
|
|
if(socket !== clientSocket) {
|
|
|
|
|
socket.write(data)
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
})
|
|
|
|
|
// 向客户端发送数据
|
|
|
|
|
clientSocket.write('hello')
|
|
|
|
|
})
|
|
|
|
|
server.on('error', (err) => {
|
|
|
|
|
console.error('Server error:', err);
|
|
|
|
|
});
|
|
|
|
|
// 把服务启用在3000端口上
|
|
|
|
|
server.listen(3000, () => {
|
|
|
|
|
console.log('Server running');
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
新建client.js文件,创建客户端
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const net = require('net')
|
|
|
|
|
// 创建TCP连接到指定的主机端口上
|
|
|
|
|
const client = net.createConnection({
|
|
|
|
|
host:'127.0.0.1',
|
|
|
|
|
port: 3000
|
|
|
|
|
})
|
|
|
|
|
// 监听TCP与服务端的连接
|
|
|
|
|
client.on('connect', () => {
|
|
|
|
|
console.log('成功连接服务器!!');
|
|
|
|
|
|
|
|
|
|
//客户端发消息给服务端
|
|
|
|
|
|
|
|
|
|
process.stdin.on('data', data => {
|
|
|
|
|
// console.log(data.toString());
|
|
|
|
|
client.write(data.toString().trim());
|
|
|
|
|
})
|
|
|
|
|
// client.write('Hi Lilei!!');
|
|
|
|
|
|
|
|
|
|
})
|
|
|
|
|
// 获取服务端的数据
|
|
|
|
|
|
|
|
|
|
client.on('data', (data) => {
|
|
|
|
|
console.log(data.toString());
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 构建UDP服务
|
|
|
|
|
|
|
|
|
|
UDP和TCP一样是在网络传输层处理数据包的,最大特点不需要连接、传输速度快、数据传输不可靠。
|
|
|
|
|
|
|
|
|
|
server.js
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const dgram = require('dgram')
|
|
|
|
|
|
|
|
|
|
const server = dgram.createSocket('udp4')
|
|
|
|
|
|
|
|
|
|
server.on('listening', () =>{
|
|
|
|
|
const address = server.address()
|
|
|
|
|
console.log(`server running ${address.address}:${address.port}`);
|
|
|
|
|
})
|
|
|
|
|
server.on('message', (msg, remoteInfo) =>{
|
|
|
|
|
console.log(`server got ${msg} form ${remoteInfo.address}:${remoteInfo.port}`);
|
|
|
|
|
})
|
|
|
|
|
server.on('error', (err) =>{
|
|
|
|
|
console.log(`server error`, err);
|
|
|
|
|
})
|
|
|
|
|
server.bind(3000)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
clent.js:
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const dgram = require('dgram')
|
|
|
|
|
|
|
|
|
|
const client = dgram.createSocket('udp4')
|
|
|
|
|
|
|
|
|
|
client.send('hello', 3000, 'localhost')
|
|
|
|
|
|
|
|
|
|
server.on('listening', () =>{
|
|
|
|
|
const address = server.address()
|
|
|
|
|
console.log(`client running ${address.address}:${address.port}`);
|
|
|
|
|
})
|
|
|
|
|
server.on('message', (msg, remoteInfo) =>{
|
|
|
|
|
console.log(`client got ${msg} form ${remoteInfo.address}:${remoteInfo.port}`);
|
|
|
|
|
})
|
|
|
|
|
server.on('error', (err) =>{
|
|
|
|
|
console.log(`client error`, err);
|
|
|
|
|
})
|
|
|
|
|
// server.bind(8000)
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
### 构建HTTP服务
|
|
|
|
|
|
|
|
|
|
基本的HTTP服务
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const http = require('http')
|
|
|
|
|
|
|
|
|
|
const hostname = '127.0.0.1'
|
|
|
|
|
|
|
|
|
|
const port = 3000
|
|
|
|
|
|
|
|
|
|
const server = http.createServer((req, res) => {
|
|
|
|
|
res.statusCode = 200,
|
|
|
|
|
res.setHeader('Content-Type', 'text/plan')
|
|
|
|
|
res.end('hello world\n')
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
server.listen(port, hostname, () => {
|
|
|
|
|
console.log(`sever running at http://${hostname}:${port}/`);
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
根据URL解析内容
|
|
|
|
|
|
|
|
|
|
```js
|
|
|
|
|
const http = require('http')
|
|
|
|
|
|
|
|
|
|
const hostname = '127.0.0.1'
|
|
|
|
|
|
|
|
|
|
const port = 3000
|
|
|
|
|
|
|
|
|
|
const server = http.createServer((req, res) => {
|
|
|
|
|
|
|
|
|
|
// 根据url 处理响应
|
|
|
|
|
const url = req.url
|
|
|
|
|
if(url === '/') {
|
|
|
|
|
res.end('hello world\n')
|
|
|
|
|
} else if(url === '/a') {
|
|
|
|
|
res.end('hello a\n')
|
|
|
|
|
} else if(url === '/b') {
|
|
|
|
|
res.end('hello nb\n')
|
|
|
|
|
} else{
|
|
|
|
|
res.statusCode = 404
|
|
|
|
|
res.end('404 not found')
|
|
|
|
|
}
|
|
|
|
|
})
|
|
|
|
|
|
|
|
|
|
server.listen(port, hostname, () => {
|
|
|
|
|
console.log(`sever running at http://${hostname}:${port}/`);
|
|
|
|
|
})
|
|
|
|
|
```
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
### 构建HTTPS服务
|
|
|
|
|
|
|
|
|
|
https原理及安装本地证书
|
|
|
|
|
|
|
|
|
|
https协议 = http协议 + SSL/TLS协议
|
|
|
|
|
|
|
|
|
|
构建方式就是和http一样就是多了一个ca安全证书
|
|
|
|
|
|
|
|
|
|
## Nodejs事件循环和多进程
|
|
|
|
|
|
|
|
|
|
事件循环的概念:在浏览器或者nodejs环境中,运行时对js脚本的调度方式就叫做事件循环
|
|
|
|
|
|
|
|
|
|
### 浏览器事件循环
|
|
|
|
|
|
|
|
|
|
因为浏览器js的作用是操作DOM,这决定了js只能是单线程,否则会带来很复杂的同步问题。比如,假定JavaScript同时有两个线程,一个线程在某个DOM节点上添加内容,另一个线程删除了这个节点,这时浏览器应该以哪个线程为准?出现冲突
|
|
|
|
|
|
|
|
|
|
任务队列:单线程就意味着所有任务需要排队,如果因为任务cpu计算量大还好,但是I/O操作cpu是闲着的。所以js就设计成了一门异步的语言,不会做无畏的等待。任务可以分成两种,一种是同步任务(synchronous),另一种是异步任务(asynchronous)。
|
|
|
|
|
|
|
|
|
|
> (1)所有同步任务都在主线程上执行,形成一个执行栈(execution context stack)。
|
|
|
|
|
>
|
|
|
|
|
> (2)主线程之外,还存在一个"任务队列"(task queue)。只要异步任务有了运行结果,就在"任务队列"之中放置一个事件。
|
|
|
|
|
>
|
|
|
|
|
> (3)一旦"执行栈"中的所有同步任务执行完毕,系统就会读取"任务队列",看看里面有哪些事件。那些对应的异步任务,于是结束等待状态,进入执行栈,开始执行。
|
|
|
|
|
>
|
|
|
|
|
> (4)主线程不断重复上面的第三步。
|
|
|
|
|
|
|
|
|
|

|
|
|
|
|
|
|
|
|
|
宏任务与微任务:JavaScript 单线程中的任务可以细分为宏任务(macrotask)和微任务(microtask)
|
|
|
|
|
|
|
|
|
|
- macrotask: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
|
|
|
|
|
|
|
|
|
|
- microtask:process.nextTick, Promise, Object.observe, MutationObserver。
|
|
|
|
|
|
|
|
|
|
> 1. 宏任务进入主线程,执行过程中会收集微任务加入微任务队列。
|
|
|
|
|
> 2. 宏任务执行完成之后,立马执行微任务中的任务。微任务执行过程中将再次收集宏任务,并加入宏任务队列。
|
|
|
|
|
> 3. 反复执行1,2步骤
|
|
|
|
|
|
|
|
|
|
每轮事件循环执行一个宏任务和所有的微任务,而且任务队列一定会保持先进先出的顺序执行。
|
|
|
|
|
|
|
|
|
|
### nodejs事件循环
|
|
|
|
|
|
|
|
|
|
当Node.js启动时会初始化event loop, 每一个event loop都会包含按如下六个循环阶段,nodejs事件循环和浏览器的事件循环完全不一样。
|
|
|
|
|
|
|
|
|
|
|
|
|
|
|
- **timers(定时器)** : 此阶段执行那些由 `setTimeout()` 和 `setInterval()` 调度的回调函数.
|
|
|
|
|
|
|
|
|
|
- **I/O callbacks(I/O回调)** : 此阶段会执行几乎所有的回调函数, 除了 **close callbacks(关闭回调)** 和 那些由 **timers** 与 `setImmediate()`调度的回调.
|
|
|
|
|
|
|
|
|
|
> setImmediate 约等于 setTimeout(cb,0)
|
|
|
|
|
|
|
|
|
|
- idle(空转), prepare : 此阶段只在内部使用
|
|
|
|
|
|
|
|
|
|
- **poll(轮询)** : 检索新的I/O事件; 在恰当的时候Node会阻塞在这个阶段
|
|
|
|
|
|
|
|
|
|
- check(检查) : `setImmediate()` 设置的回调会在此阶段被调用
|
|
|
|
|
|
|
|
|
|
- close callbacks(关闭事件的回调): 诸如 `socket.on('close', ...)` 此类的回调在此阶段被调用
|
|
|
|
|
|
|
|
|
|
在事件循环的每次运行之间, Node.js会检查它是否在等待异步I/O或定时器, 如果没有的话就会自动关闭.
|
|
|
|
|
|
|
|
|
|
> 如果event loop进入了 poll阶段,且代码未设定timer,将会发生下面情况:
|
|
|
|
|
>
|
|
|
|
|
> - 如果poll queue不为空,event loop将同步的执行queue里的callback,直至queue为空,或执行的callback到达系统上限;
|
|
|
|
|
> - 如果poll queue为空,将会发生下面情况:
|
|
|
|
|
> - 如果代码已经被setImmediate()设定了callback, event loop将结束poll阶段进入check阶段,并执行check阶段的queue (check阶段的queue是 setImmediate设定的)
|
|
|
|
|
> - 如果代码没有设定setImmediate(callback),event loop将阻塞在该阶段等待callbacks加入poll queue,一旦到达就立即执行
|
|
|
|
|
>
|
|
|
|
|
> 如果event loop进入了 poll阶段,且代码设定了timer:
|
|
|
|
|
>
|
|
|
|
|
> - 如果poll queue进入空状态时(即poll 阶段为空闲状态),event loop将检查timers,如果有1个或多个timers时间时间已经到达,event loop将按循环顺序进入 timers 阶段,并执行timer queue.
|
|
|
|
|
|
|
|
|
|
### nodejs多进程
|