You cannot select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

328 lines
11 KiB
Markdown

1 year ago
# 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主线程不断重复上面的第三步。
![1700729252911](D:\我的文档\Documents\WeChat Files\wxid_gepw3wo1z3rz21\FileStorage\Temp\1700729252911.png)
宏任务与微任务JavaScript 单线程中的任务可以细分为宏任务macrotask和微任务(microtask)
- macrotask: script(整体代码), setTimeout, setInterval, setImmediate, I/O, UI rendering。
- microtaskprocess.nextTick, Promise, Object.observe, MutationObserver。
> 1. 宏任务进入主线程,执行过程中会收集微任务加入微任务队列。
> 2. 宏任务执行完成之后,立马执行微任务中的任务。微任务执行过程中将再次收集宏任务,并加入宏任务队列。
> 3. 反复执行12步骤
每轮事件循环执行一个宏任务和所有的微任务,而且任务队列一定会保持先进先出的顺序执行。
### 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多进程