什么是协议
协议就是一种语言,好比我们人之间说话一样,使用双方都能理解的语言(协议)进行交流(通信)。
- HTTP协议就是一种语言,严格来说就是机器语言,非常弱智,理解能力一般
- 该语言是专门用于客户端(浏览器)和服务端进行通信而设计出来的
什么是HTTP
HTTP是一种能够获取如HTML这样的网络资源的protocol(通信协议)。它是在web上进行数据交换的基础,是一种client-server协议,也就是说,请求通常是由像浏览器这样的接受方发起的。一个完整的web文档通常是由不同的子文档拼接而成的,像是文本、布局描述、图片、视频、脚本等。
例如,当你输入:https://www.baidu.com/
浏览器会把这个请求解析尾HTTP协议的语言报文发送给服务器:
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
- GET / HTTP/1.1:请求首行
- GET:请求方法,常见有GET(获取)、POST(添加)、PUT(更新)、DELETE(删除)
- /:表示请求路径,所有的请求路径都以 / 开头,也就是域名后的部分
- 比如 /a、/b
- HTTP/1.1:使用的什么语言,以及语言的版本号
请求首行下面的是请求首部字段,不同的字段有不同的含义:
Host: www.baidu.com :告诉对方访问的域名是什么
Connection: keep-alive:告诉对方要进行长链接
Cache-Control: max-age=0:它是HTTP缓存相关的头部字段
如果有请求体(一般是表单数据),那么请求方法对应的就是PUT、POST之类的。请求体数据会和首部字段隔行放置。
GET / HTTP/1.1
Host: www.baidu.com
Connection: keep-alive
Cache-Control: max-age=0
# JSON格式的请求体:application/json,常用于接口交互
{"username":"admin","password":123456}
# form-urlencoded 格式的请求体:application/x-www-form-urlencoded,常用于表单提交
username=admin&password=123456
# 带有文件的请求体:multipart/form-data
×××
HTTP响应报文
服务器接收到客户端请求的HTTP请求报文后,解析处理,最后发送响应给客户端。
响应给客户端的数据也要使用HTTP协议。
举个例子:
# HTTP/1.1 状态码 状态短语
HTTP/1.1 200 OK
Server: Apache
Content-Length: 123
×××: ×××
Content-Type: text/css
<h1>hello</h1> # HTML格式的字符串、图片、JSON数据、各种各样的数据,一定要和Content-Type定义的格式一致,才能去解析
使用Node来构建一个简单的HTTP服务
const net = require("net"); //Node中专门用来TCP网络通信的模块
// 创建一个TCP服务器
const server = net.createServer((socket) => {
// 当有客户端请求进来的时候会执行这里的回调函数
// 任何客户端请求进来都会执行这里,但是不同的客户端的socket是不一样的
// socket只是一个名字,通常表示套接字,插座,连接线缆的窟窿眼儿
// socket是客户端
// 通过socket我们可以拿到对方的来源位置:比如端口号、IP地址之类的信息或者使用socket给对方发送消息
console.log("有客户端请求进来了");
// 请求相关的数据对象,把请求报文中解析道的数据放到这个对象里面方便操作
const request = {};
// 响应相关的数据对象
const response = {};
// 接收客户端请求报文数据
// 我们不能一次性的拿到客户端请求的数据,因为客户端的发送的数据大小无法确定,所以必须用流的方式来接收
const data = [];
socket.on("data", (chunk) => {
// 本次接收到的数据块
data.push(chunk);
});
socket.on("end", () => {
// 数据接收完毕会触发end事件
const reqHttpMessage = data.toString(); // 原始的HTTP请求报文
// 为了方便处理,通常会把请求方法、请求地址、请求头解析出来
const arr = reqHttpMessage.split("\r\n");
const httpFirstLine = arr[0];
const [method, path, httpVersion] = reqHttpMessage.split(" ");
request.method = method;
request.path = path;
request.httpVersion = httpVersion;
console.log(request);
});
// 测试一下
// 响应首行是必须的,不能少,响应头部字段都是可短的
socket.end(`
HTTP/1.1 200 OK
Server: Hello
hello world
`);
});
// 启动TCP服务
server.listen(4000, () => {
console.log("启动成功", server.address());
});
// Node中其实内置了一个模块:http,它是基于net模块开发的。已经把请求和响应的协议都处理好了,上层使用更加方便
const http = require("http");
const fs = require("fs");
// 为了方便我们使用,它内部封装提供了request和response对象
const server = http.createServer((req, res) => {
// 请求对象,里面的数据基本都是解析HTTP请求报文而来的
console.log(req.method);
console.log(req.url);
console.log(req.httpVersion);
// 响应对象:提供了响应相关的API
// 它在内部会自动把你发送的数据包装尾HTTP响应报文
// res.end('hello world')
// res.end(`
// <h1>hello world</h1>
// `);
// res.end(`
// <!DOCTYPE html>
// <html lang="en">
// <head>
// <meta charset="UTF-8">
// <meta http-equiv="X-UA-Compatible" content="IE=edge">
// <meta name="viewport" content="width=device-width, initial-scale=1.0">
// <title>Document</title>
// </head>
// <body>
// <h1>hello world 111</h1>
// </body>
// </html>
// `)
// 设计一个规则,'/'给你首页,'/foo'给你data.json
// 所谓的请求路径只不过是一个标识而已
const url = req.url;
if (url === "/") {
// 发送一个文件,本质还是发送一个字符串
fs.readFile("./index1.html", (err, data) => {
if (err) {
// 如果发生错误,说明该文件不存在,应该告诉客户端没有这个资源
// HTTP/1.1 404 NOTFOUND
res.statusCode = 404;
res.end();
return;
}
console.log(data.toString());
res.end(data); // 可以直接发送Buffer二进制数据,也可以发送字符串
});
} else if (url === "/foo") {
// 发送一个文件,本质还是发送一个字符串
fs.readFile("./data.json", (err, data) => {
if (err) {
// 如果发生错误,说明该文件不存在,应该告诉客户端没有这个资源
// HTTP/1.1 404 NOTFOUND
res.statusCode = 404;
res.end();
return;
}
console.log(data.toString());
res.end(data); // 可以直接发送Buffer二进制数据,也可以发送字符串
});
} else {
res.statusCode = 404;
res.setHeader("Content-Type", "text/plain;charset=utf-8");
res.end("没有数据");
}
});
server.listen(4000, () => {
console.log("http://localhost:4000/");
});