一起养成写作习惯!这是我参与「掘金日新计划 · 4 月更文挑战」的第14天,点击查看活动详情。
net 模块
通过研究 net 模块,可以帮助我们对网络请求有一个更深刻的认识。
回顾http请求
http请求是建立在TCP/IP协议上的。 tcp/ip 负责建立连接,htpp 协议则是规定请求的格式和文件类型。
http请求分为两种模式
- 普通模式:
通过三次握手建立连接,完成请求和响应,通过四次挥手断开连接。握手和挥手的都是操作系统完成的,我们只需要关心请求和完成响应。
- 长连接模式:
我们打开一个网页,会一次性请求很多的资源。如果使用普通模式的话,就会产生很多次的连接建立和断开的过程,造成响应时间增加很多。
在 http 请求头中将 Connection设为 keep-alive,表示开启了长连接。那么这次请求就不会自动的断开,客户端可以在一次连接中发送多个请求。
net
net 是一个通信模块,可以实现:进程间的通信 IPC,网络通信 TCP/IP。
创建客户端
// 开启一个socket
const net = require("net")
// 接收的数据
let receive = null;
// socket 是操作系统里的一个特殊的文件,跟网卡关联。
// 在node中表现为双工流对象
// 通过向流写入数据、获取数据
const socket = net.createConnection({
host: "juejin.cn", // 主机地址
port: 80
}, () => {
console.log('连接成功');
})
socket.write(`GET / HTTP/1.1
Host: juejin.cn
Connection: keep-alive
`);
socket.on("close", () => {
console.log(receive.body);
console.log('请求结束');
})
// 得到响应的数据,进行处理
socket.on("data", chunk => {
// 根据响应头的 Content-Length 来判断是否传输完成
const response = chunk.toString('utf-8');
if (!receive) {
receive = parseResponse(response);
if (!isOver()) {
socket.end();
}
return;
}
receive.body += response;
if (!isOver()) {
socket.end();
return;
}
})
// 辅助函数
/**
* 提炼出响应字符串的消息头和消息体
* @param {*} response
*/
function parseResponse(response) {
const index = response.indexOf("\r\n\r\n");
const head = response.substring(0, index);
const body = response.substring(index + 2).trimStart();
const headParts = head.split('\r\n');
const headArr = headParts.slice(1).map(str => {
return str.split(":").map(s => s.trim())
})
const header = headArr.reduce((a, b) => {
a[b[0]] = b[1];
return a
}, {})
return {
header,
body
}
}
// 判断是否接收完成
function isOver() {
// 需要接收的消息体的总字节数
const contentLength = +receive.header["Content-Length"];
const curReceiveLength = Buffer.from(receive.body, 'utf-8').byteLength;
return curReceiveLength >= contentLength;
}
创建服务器
const net = require('net');
const server = net.createServer();
server.listen(8081);//服务器监听端口8081
server.on('listening', ()=>{
console.log("server is listening 8081");
})
处理请求
// 当连接到来会触发该函数, 得到一个socket对象
server.on("connection", socket => {
console.log('接收连接');
socket.on("data", chunk => {
console.log(chunk.toString('utf-8'));
socket.write(`HTTP/1.1 200 OK
<!doctype html>
<html lang='en'>
<head>
</head>
<body>
<h1>你好</h1>
</body>
</html>`);
socket.end();
})
socket.on("end", ()=>{
console.log('连接关闭');
})
})