Skip to content

Latest commit

 

History

History
executable file
·
500 lines (341 loc) · 8.4 KB

6. 网络编程.md

File metadata and controls

executable file
·
500 lines (341 loc) · 8.4 KB
marp paginate style
true
true
h1 { color: #0bb8e8; }
<style scoped> h1,h2 { color: #0bb8e8; text-align: center } h2 { text-align: right } </style>

Node.js 基础

六、网络编程

⭐ by calidion


Node.js 的优势能力

Node.js 让网络开发变的非常容易 让很多原来必须使用 C++开发的项目有机会通过 Node.js 来实现。 Node.js 编写网络应用的优势主要是在它简化了网络连接与管理的代码


网络基础知识

  1. TCP/IP 协议(互联网的基础设施)

  1. Socket
  2. IO 模型(select, poll, epoll, Kqueue, IOCP)

Socket

  1. 是一种连接方式
  2. 基于 IP+PORT

Socket 编程

  1. 是指基于 IP+PORT 的方式进行编程的方式
  2. 典型的 Socket 编程:TCP/UDP
  3. TCP/UDP 之上的编程也都是 Socket 编程

网络编程基础--IO 模型

  1. select/poll 一种轮询检查 IO 的机制 select 来自 BSD 分支 poll 来自 AT & T 分支

  2. epoll/kqueue 基于事件的 IO 机制,不再轮询,提高了效率 epoll 是 Linux 的模型 kqueue 是 BSD 的模型,包括 Darwin

  3. IOCP 多线程异步 IO 模型, 事先会创建线程池用于请求的处理,是 Windows 平台的


libuv

  1. 是 Cross-platform asynchronous I/O,即 libuv 是将不同的平台接口进行统一,并提供一致的接口给上层,屏蔽 OS 系统造成的差异
  2. Node.js 使用 libuv 在不同的平台采用最高效的 IO 模型(包括 epoll, IOCP, kqueue)
  3. libuv 最初是给 Node.js 开发使用的

select/poll 的问题

  1. 最大并发数限制 一个进程的并发数受限(FD_SIZE = 1024) 修改 FD_SIZE 无法改善
  2. 效率问题 每次扫描所有的 FD 连接越多,速度越慢
  3. 内存拷贝 用户态与核态互拷数据
  4. poll 取消了并发数限制,但是没有解决其它 2 个问题

异步与同步、阻塞与非阻塞

  1. 异步与同步主要是相对于代码执行来讲的
  2. 阻塞与非阻塞主要是相对于进程/线程来讲的
  3. 异步/同步/阻塞/非阻塞IO实际上是相对于 IO 调用来讲的 当 IO 调用会阻塞/非阻塞线程时,那么这个 IO 就是阻塞/非阻塞 IO 当 IO 调用会导致代码同步/异步时,那么这个 IO 就是同步/异步 IO

关系表

代码 线程/进程
同步 阻塞
异步 非阻塞

阻塞 IO 与非阻塞 IO

  1. 阻塞 IO


  1. 非阻塞 IO


  1. 异步 IO


IO 模型对比


异步编程与异步 IO 的关系

  1. 异步编程是基于异步调用的

  2. 代码的异步调用的原因可以是 a. 进程性/线程的异步(setImmediate) b. 定时器引起的异步(setTimeout) c. 异步 IO 引起的异步(socket 编程) d. 事件引起的异步

  3. 以上形式都可以导致异步调用,但是不一定是在语言层面上就是异步的


异步 IO 与 Node.js 的结合

  1. javascript 在是事件驱动的语言
  2. javascript 语言本身并没有多线程机制
  3. javascript 的事件回调函数机制非常成熟
  4. 异步+单线程有一些明显的好处 减少了线程切换成本 避免了等待 有更高的效率

理解异步/同步

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");

代码说明

  1. 异步需要多线程支持
  2. JS 里自定义的函数默认是同步的
  3. 回调与同步/异步无关

多进(线)程与并行的概念

多进程: 就是一个操作系统(OS),可以同时运行多个任务 多线程: 一个总任务,再分解成多个任务,同时执行。

并行: 就是二个或者二个以上的任务同时执行。


Node.js 网络编程

  1. 支持协议 a. tcp b. udp c. http

  2. 理解无连接/有连接 a. 有连接是指链跟建立需要又方确认, tcp 以及 tcp 之上的协议都是有连接的协议 b. 无连接是指建立链接不需要确认,udp 协议属于这类协议


  1. 理解有状态与无状态 a. 有状态是指链接成功后,链接双方一直维护相互的用户状态(tcp)
  request1(unknown)
 A ---> B
  request2(know)
 A ---> B

b. 无状态是指链接成功后,链接双方不能直接通过链接本身识别双方(http)

  request1(unknown)
 A ---> B
  request2(unknown)
 A ---> B

创建 TCP 服务器

  1. 引入 net 包
const net = require("net");
  1. 创建服务器
const server = net.createServer((socket) => {
  socket.end('Hello from tcp server!\n');
});

  1. 侦听端口
server.listen(process.env.NODE_PORT || 8888);

其中: net 包提供了基本的 TCP 接口包 net.createServer 创建一个 net.Server 类的对象 使用 listen 方法侦听端口


net.Server 类

  1. 用于创建 TCP/IPC 服务器
new net.Server([options][, connectionListener])
//或者
net.createServer([options][, connectionListener])

2.可能响应的事件: a. connection:外部 socket 接入事件 b. listening: 侦听事件 c. close: 关闭事件 d. error: 错误事件


connection 事件

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);

listening 事件

server.on("listening", () => {
        console.log("server is listening");
        console.log(arguments);
});

比 listen 的回调优先级高。

error 事件

server.on('error', (err) => {
  // handle errors here
  throw err;
});

close 事件

会在所有连接关闭后发出

server.on('close', (err) => {
console.log("server is closed!");
});

net.Socket 类

  1. 获取远程地址与端口 .remoteAddress .remotePort

  2. 取消 TCP 缓存数据(Nagle 算法) .setNoDelay

  3. 发送数据 .write


net.Socket 重要事件

  1. data 表示有数据进入

  2. end 表示数据传输完成

  1. 其它事件 error, connect, close

创建 UDP 服务器

  1. 引入 dgram 包
const dgram = require('dgram');
  1. 创建 udp 服务器
const server = dgram.createSocket('udp4');
  1. 创建 message 事件的接收器
server.on('message', (msg, rinfo) => {
  console.log(`server got: ${msg} from ${rinfo.address}:${rinfo.port}`);
});

  1. 设置侦听事件的响应
server.on('listening', () => {
  const address = server.address();
  console.log(`server listening ${address.address}:${address.port}`);
});
  1. 绑定到端口上
server.bind(4333);

完成。


创建 UDP 客户端

  1. 引入 dgram 包
const dgram = require('dgram');
  1. 创建 udp 服务器
const client = dgram.createSocket('udp4');
  1. 发送消息
const message = Buffer.from('Udp 客户端请求');
client.send(message, 4333, 'localhost', (err) => {
  client.close();
});

UDP 收发消息

  1. 服务器发消息
server.send("message '" + msg + "' received!",
rinfo.port, rinfo.address);
  1. 客户端收消息
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 服务器

  1. 引入 http 包
const http = require('http');
  1. 创建服务器
const server = http.createServer((req, res) => {
  res.end("Hello world!\n");
});
  1. 侦听端口
server.listen(4333);

访问测试

$> curl http://127.0.0.1:4333
Hello world!

最简单的 HTTP 服务器完成!

下面命令可以看到协议的一些交互信息:

curl -v http://127.0.0.1:4333