回顾http请求
普通模式
长连接模式
注: 图中应该是四次挥手
长连接模式就是客户端与服务器之间的持续数据传输,普通模式会在服务器做出响应后进行四次挥手
而长连接就像是,客户端给服务打电话,告诉服务器你先别挂,我还有话没说,服务器也告诉客户端我不会挂电话,我等你但是过啦一会客户端还是没有回信息就会挂掉也有可能客户端等啦会服务器没有回话也会挂掉,或者客户端或服务端没有啥话可说啦就吧电话挂断啦, 如下图红框中的keep-alive 就是长连接模式
补充:http请求是建立在tcp/ip协议上的 三次握手与四次握手是tcp/ic协议, http是如何发送请求,请求的格式又是什么
net模块能干什么
net模块是一个通信模块
利用它可是实现
- 进程间的通讯 IPC
- 网络通信 TCP/IP
创建客户端
net.createConnection(options[, connectListener])官网上的解释
它的返回值是个 net.Socket在文档中搜索net.Socket,我发现它是一个类 如下图
从部分事件可以看出,这是一个流啊这说明客户端与服务之间交流数据是通过流式操作的,这不就和上节课写的文件流联系起来啦
连接服务器
```javascript const net = require(‘net’); const socket = net.createConnection( { host: ‘duyi.ke.qq.com’, port: 80, }, () => { console.log(‘连接成功’); } );
socket.on(‘data’, (chunk) => { console.log(chunk) });
但是运行结果是 并没有接受到数据<br />![image.png](https://cdn.nlark.com/yuque/0/2021/png/1376603/1627988994038-7d71dd1f-61b9-4d55-a1bc-534f6b5f3456.png#align=left&display=inline&height=170&margin=%5Bobject%20Object%5D&name=image.png&originHeight=170&originWidth=1118&size=52974&status=done&style=stroke&width=1118)
<a name="tgQye"></a>
### 为何接收不到数据
http协议,客户端需要给服务器发送请求,服务器才会响应, 而我们刚好就是卡在这一步,为啥呢明明我创建啦客户端并设置服务器的域名与端口号不连接呢,之前我是用的ajax那些封装好的,而net模块偏向于底层,<br />当我尝试随意往流里写入数据
```javascript
socket.write('hello !!!')
socket.on('data', (chunk) => {
console.log(chunk.toString('utf-8'))
});
虽然返回啦数据http状态码却是400 而且这又啥,这是http协议规定的返回信息的格式,我们之间用ajax请求到的数据都是经过处理过后的
响应行:
HTTP/1.1 400 Bad Request
响应头
Server: stgw/1.3.12.3_1.13.5
Date: Tue, 03 Aug 2021 11:15:05 GMT
Content-Type: text/html
Content-Length: 181
Connection: close
响应体
<html>
<head><title>400 Bad Request</title></head>
<body bgcolor="white">
<center><h1>400 Bad Request</h1></center>
<hr><center>stgw/1.3.12.3_1.13.5</center>
</body>
</html>
为哈服务器会响应400呢你看服务器响应信息都是有格式,按发送请求也是格式的
发送请求的格式
格式为:
socket.write(`请求行
请求头
请求体`);
//注意这里都是换行,没有空格,空格是识别不了的
而上面给服务发送请求是个这个格式吗,而是直接发送一段文本,服务肯定不认识,那不就返回400啦
这里的服务器只认http协议
如何在tcp/ip 协议书写http的get请求呢
socket.write(`GET / HTTP/1.1
Host: duyi.ke.qq.com
Connection: keep-alive
`); // get 请求的请求体是空的 但是请求头到请求体的之间的两个换行是必须要有的,不然服务器以为你还信息没传过来会一直等着客户端将请求体传过来
请求结果
连接成功
HTTP/1.1 302 Moved Temporarily
Date: Tue, 03 Aug 2021 11:41:28 GMT
Content-Type: text/html
Content-Length: 138
Connection: keep-alive
Server: nginx
Location: https://duyi.ke.qq.com/
X-Request-Id: 2ba8fab7-2444-44da-bd75-2d55b859909c
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
请求行:
HTTP/1.1 302 Moved Temporarily
请求头// 请求头与请求体相差两行
Date: Tue, 03 Aug 2021 11:41:28 GMT
Content-Type: text/html
Content-Length: 138
Connection: keep-alive
Server: nginx
Location: https://duyi.ke.qq.com/
X-Request-Id: 2ba8fab7-2444-44da-bd75-2d55b859909c
请求体:
<html>
<head><title>302 Found</title></head>
<body>
<center><h1>302 Found</h1></center>
<hr><center>nginx</center>
</body>
</html>
注意:请求头中Content-Length: 138 会告诉我们请求体会有多少行
完整代码
const net = require('net');
const socket = net.createConnection(
{
host: 'duyi.ke.qq.com',
port: '80',
},
() => {
console.log('连接成功');
}
);
/**
* 格式化response
* 将response-header 与 response-body
* {
* header: response-header
* body: response-body
* }
* header 与 body 都会间隔一个\r\n\r\n
*/
function formatResponse(response) {
// console.log(response, 'response')
const index = response.indexOf('\r\n\r\n'); // 请求头与请求体相差两行
const head = response.substring(0, index);
const body = response.substring(index + 2);
/**格式化header */
const headerParts = head.split('\r\n');
const headerArr = headerParts.slice(1).map((ele) => {
return ele.split(':').map((str) => str.trim());
});
const header = headerArr.reduce((acc, cur) => {
acc[cur[0]] = cur[1];
return acc;
}, {});
return {
header,
body: body.trimStart(),
};
}
/**
* 是否结束
*/
function isOver() {
const contentLength = +receive.header['Content-Length']; //Content-Length 该属性显示请求体有多少行
const curReceivedLength = Buffer.from(receive.body, 'utf-8').byteLength;
return contentLength < curReceivedLength;
}
let receive = null;
/**
* 监听数据流
*/
socket.on('data', (chunk) => {
const response = chunk.toString('utf-8');
if (!receive) {
receive = formatResponse(response);
if (isOver()) {
socket.end();
}
return;
}
receive += response.body;
if (isOver) {
socket.end();
return;
}
});
/**
* 写入header
*/
socket.write(`GET / HTTP/1.1
Host: duyi.ke.qq.com
Connection: keep-alive
`);
/** */
socket.on('close', () => {
console.log('结束啦')
console.log(receive.header, 'header')
console.log(receive.body, 'body')
})
创建服务器
官网上的解释
完整代码
示意图
socket是啥
当客户端连接服务器,它们之间是需要交流的,服务器使用socket去和客户端交流,来一个客服端服务器会生成一个新的socket去和客户端对接。
socket就像是客服,来一个客户就会有一个客服来对接
代码
const net = require('net');
const path = require('path');
const fs = require('fs');
/**
* 创建服务器
*/
const server = net.createServer();
/**
* 监听端口
*/
server.listen('9527');
/**
* 开始监听端口后触发的事件
*/
server.on('listening', () => {
console.log('server listen 9527');
});
/**
* 当某个连接到来时,触发该事件
* 来个一个客户端便会产生一个新的socket
* socket就像是客服,来一个客户就会有一个客服进行对接
*/
server.on('connection', (socket) => {
console.log('有客户端连接服务器');
socket.on('data', async (chunk) => {
const imgPath = path.resolve(__dirname, './1.jpg');
const imgBuffer = await fs.promises.readFile(imgPath);
const headBuffer = Buffer.from(`HTTP/1.1 200 OK
Content-Type: image/jpeg
`,
'utf-8'
);
const sendBuffer = Buffer.concat([headBuffer, imgBuffer]);
socket.write(sendBuffer)
socket.end();
});
});
server.on('end', () => {
console.log('连接结束')
})