# 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。

- 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多进程