marp | paginate | style |
---|---|---|
true |
true |
h1 {
color: #0bb8e8;
}
|
Node.js 让网络开发变的非常容易 让很多原来必须使用 C++开发的项目有机会通过 Node.js 来实现。 Node.js 编写网络应用的优势主要是在它简化了网络连接与管理的代码
- TCP/IP 协议(互联网的基础设施)
- 是一种连接方式
- 基于 IP+PORT
- 是指基于 IP+PORT 的方式进行编程的方式
- 典型的 Socket 编程:TCP/UDP
- TCP/UDP 之上的编程也都是 Socket 编程
-
select/poll 一种轮询检查 IO 的机制 select 来自 BSD 分支 poll 来自 AT & T 分支
-
epoll/kqueue 基于事件的 IO 机制,不再轮询,提高了效率 epoll 是 Linux 的模型 kqueue 是 BSD 的模型,包括 Darwin
-
IOCP 多线程异步 IO 模型, 事先会创建线程池用于请求的处理,是 Windows 平台的
- 是 Cross-platform asynchronous I/O,即 libuv 是将不同的平台接口进行统一,并提供一致的接口给上层,屏蔽 OS 系统造成的差异
- Node.js 使用 libuv 在不同的平台采用最高效的 IO 模型(包括 epoll, IOCP, kqueue)
- libuv 最初是给 Node.js 开发使用的
- 最大并发数限制 一个进程的并发数受限(FD_SIZE = 1024) 修改 FD_SIZE 无法改善
- 效率问题 每次扫描所有的 FD 连接越多,速度越慢
- 内存拷贝 用户态与核态互拷数据
- poll 取消了并发数限制,但是没有解决其它 2 个问题
- 异步与同步主要是相对于
代码执行
来讲的 - 阻塞与非阻塞主要是相对于进程/线程来讲的
异步/同步/阻塞/非阻塞IO
实际上是相对于 IO 调用来讲的 当 IO 调用会阻塞/非阻塞线程时,那么这个 IO 就是阻塞/非阻塞 IO 当 IO 调用会导致代码同步/异步时,那么这个 IO 就是同步/异步 IO
代码 | 线程/进程 |
---|---|
同步 | 阻塞 |
异步 | 非阻塞 |
- 阻塞 IO
- 非阻塞 IO
- 异步 IO
-
异步编程是基于异步调用的
-
代码的异步调用的原因可以是 a. 进程性/线程的异步(setImmediate) b. 定时器引起的异步(setTimeout) c. 异步 IO 引起的异步(socket 编程) d. 事件引起的异步
-
以上形式都可以导致异步调用,但是不一定是在语言层面上就是异步的
- javascript 在是事件驱动的语言
- javascript 语言本身并没有多线程机制
- javascript 的事件回调函数机制非常成熟
- 异步+单线程有一些明显的好处 减少了线程切换成本 避免了等待 有更高的效率
setImmediate(() => {
console.log("1. async func executed");
});
function setSyncExecute(callback) {
// 表示执行时间
callback(sum);
}
setSyncExecute(() => {
console.log("2. sync func executed");
}, 1000);
console.log("3. after sync");
- 异步需要多线程支持
- JS 里自定义的函数默认是同步的
- 回调与同步/异步无关
多进程: 就是一个操作系统(OS),可以同时运行多个任务 多线程: 一个总任务,再分解成多个任务,同时执行。
并行: 就是二个或者二个以上的任务同时执行。
-
支持协议 a. tcp b. udp c. http
-
理解无连接/有连接 a. 有连接是指链跟建立需要又方确认, tcp 以及 tcp 之上的协议都是有连接的协议 b. 无连接是指建立链接不需要确认,udp 协议属于这类协议
- 理解有状态与无状态 a. 有状态是指链接成功后,链接双方一直维护相互的用户状态(tcp)
request1(unknown)
A ---> B
request2(know)
A ---> B
b. 无状态是指链接成功后,链接双方不能直接通过链接本身识别双方(http)
request1(unknown)
A ---> B
request2(unknown)
A ---> B
- 引入 net 包
const net = require("net");
- 创建服务器
const server = net.createServer((socket) => {
socket.end('Hello from tcp server!\n');
});
- 侦听端口
server.listen(process.env.NODE_PORT || 8888);
其中: net 包提供了基本的 TCP 接口包 net.createServer 创建一个 net.Server 类的对象 使用 listen 方法侦听端口
- 用于创建 TCP/IPC 服务器
new net.Server([options][, connectionListener])
//或者
net.createServer([options][, connectionListener])
2.可能响应的事件: a. connection:外部 socket 接入事件 b. listening: 侦听事件 c. close: 关闭事件 d. error: 错误事件
const net = require("net");
const server = net.createServer((socket) => {
});
server.on("connection", (socket) => {
// 无id
console.log("socket id = " + socket.id);
console.log("new socket is coming");
});
server.listen(process.env.NODE_PORT || 8888);
server.on("listening", () => {
console.log("server is listening");
console.log(arguments);
});
比 listen 的回调优先级高。
server.on('error', (err) => {
// handle errors here
throw err;
});
会在所有连接关闭后发出
server.on('close', (err) => {
console.log("server is closed!");
});
-
获取远程地址与端口 .remoteAddress .remotePort
-
取消 TCP 缓存数据(Nagle 算法) .setNoDelay
-
发送数据 .write
-
data 表示有数据进入
-
end 表示数据传输完成
- 其它事件 error, connect, close
- 引入 dgram 包
const dgram = require('dgram');
- 创建 udp 服务器
const server = dgram.createSocket('udp4');
- 创建 message 事件的接收器
server.on('message', (msg, rinfo) => {
console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});
- 设置侦听事件的响应
server.on('listening', () => {
const address = server.address();
console.log(`server listening ${address.address}:${address.port}`);
});
- 绑定到端口上
server.bind(4333);
完成。
- 引入 dgram 包
const dgram = require('dgram');
- 创建 udp 服务器
const client = dgram.createSocket('udp4');
- 发送消息
const message = Buffer.from('Udp 客户端请求');
client.send(message, 4333, 'localhost', (err) => {
client.close();
});
- 服务器发消息
server.send("message '" + msg + "' received!",
rinfo.port, rinfo.address);
- 客户端收消息
client.on("message", (msg, rinfo) => {
console.log(`client got: ${msg} from ${rinfo.address}:${rinfo.port}`);
console.log("message from server, received");
console.log("message is" + msg);
client.close();
});
- 引入 http 包
const http = require('http');
- 创建服务器
const server = http.createServer((req, res) => {
res.end("Hello world!\n");
});
- 侦听端口
server.listen(4333);
$> curl http://127.0.0.1:4333
Hello world!
最简单的 HTTP 服务器完成!
下面命令可以看到协议的一些交互信息:
curl -v http://127.0.0.1:4333