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