Node之net模块
net 模块是建立在 tcp/ip 协议上的。同时还可以进行进程间的通信 ipc 。我们之前的 http 请求是一次请求一次响应。普通模式是三次握手之后进行一次请求和响应然后第四次挥手。长连接模式就是在握手和挥手之间不进行中断,直到客户端或者服务器有一方提出中断,使用 keep-alive连接方式。
而 tcp/ip 可以在握手和挥手之间传递任意数据,不受限制,不需要消息头,消息体。握手和挥手也是 tcp/ip 上的内容,它们在操作系统上被执行。我们只能在浏览器上看到请求和响应。
js
const net = require('net')
const socket = net.createConnection({
host: 'duyi.ke.qq.com',
port: 80
}, () => {
console.log('连接成功')
})
socket.on('data', chunk => {
console.log(chunk.toString('utf-8'))
})
socket.write(`GET / HTTP/1.1
Host: duyi.ke.qq.com
Connection: keep-alive
`)
可以通过 net.createConnection 的方式创建一个客户端。会返回一个双工流 socket 。它是一个特殊的文件,与网卡相关联,可以随时写入读取。在进行读取前必须先写入数据。里面可以传入两个参数,第一个参数是配置对象,第二个参数是监听函数,它可以在完成连接之后调用。
这是因为 http 协议有固定的模式和格式。必须先进行请求然后才能拿到响应结果。请求的格式包括请求行,请求头,请求体。请求行一个换行到请求头,然后两个换行到请求体,请求体可以没有,但必须进行两次换行。
js
const net = require('net')
const socket = net.createConnection({
host: 'duyi.ke.qq.com',
port: 80
}, () => {
console.log('连接成功')
})
function parseResponse(response) {
const index = response.indexOf('\r\n\r\n');
const head = response.substring(0, index);
const body = response.substring(index + 2);
const headParts = head.split('\r\n');
const headerArray = headParts.slice(1).map(str => {
return str.split(':').map(s => s.trim())
})
const header = headerArray.reduce((a, b) => {
a[b[0]] = b[1];
return a;
}, {})
return {
header,
body: body.trimStart()
}
}
function isOver() {
const contentLength = receive.header['Content-Length'];
const curReceivedLength = Buffer.from(receive.body, 'utf-8').byteLength;
console.log(contentLength, curReceivedLength);
return curReceivedLength > contentLength;
}
let receive = null;
socket.on('data', chunk => {
const response = chunk.toString('utf-8')
if (!receive) {
//第一次接受数据
receive = parseResponse(response)
if (isOver()) {
socket.end()
}
return;
}
receive.body += response;
if (isOver()) {
socket.end()
return;
}
console.log('来自服务器的消息');
});
socket.write(`GET / HTTP/1.1
Host: duyi.ke.qq.com
Connection: keep-alive
`);
socket.on('close', () => {
console.log(receive.body)
console.log('关闭了');
});
我们将 header 里面的东西提取出来,变成一个对象。并找到对象里的 Content-Length 然后看是否当前的字符长度达到了要求的长度。如果第一次已经达到,就直接结束,如果没有达到,就在 body 里不断加入拿到的响应结果加进去,直到达到要求的字符长度。并在最后结束的时候将 body 输出。
js
const fs = require('fs');
const path = require('path');
const server = net.createServer();
server.listen(8000);
server.on('listening', () => {
console.log("server listen 8000")
})
server.on("connection", (socket) => {
console.log("有新的连接");
socket.on("data", async chunk => {
const filename=path.resolve(__dirname, "./myfiles/avatar.jpeg");
const bodyBuffer = await fs.promises.readFile(filename);
const headBuffer = Buffer.from(`HTTP/1.1 200 OK
Content-Type: image/jpeg
`,
"utf-8"
);
const result = Buffer.concat([headBuffer, bodyBuffer]);
socket.write(result);
socket.end();
});
});
server.on("close", () => {
console.log("server close")
});
创建一个服务器,可以传入一个监听函数,表示服务器是否创建。服务器需要监听一个端口,并且在监听之后可以通过 listening 事件得知。
当与客户端成功连接之后,会创建一个 socket ,即使是同一个客户端断开之后,也会重新创建一个 socket ,socket 在一次连接过后或者长时间断开之后销毁。使用 connection 可以监听连接。
可以读取客户端传来的 socket ,然后再写入 socket 作为响应结果。
在连接结束之后,关闭服务器。