举爪爪打招呼

很高兴你点开这篇文章✨

这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀

👍 点赞 ⭐ 收藏 💬 评论

前言

HTTP协议是互联网应用层的核心协议,也是我们日常访问网页、调用接口的基础。但很多初学网络编程的同学,只停留在“知道HTTP是啥”的阶段,却没真正搞懂它的请求响应逻辑,也没亲手实现过一个HTTP服务器。

💡 : 本篇学习笔记,从HTTP的基础概念讲起,带你拆解HTTP请求与响应报文的完整格式,再手把手带你实现一个最简单的HTTP服务器,让你从理论到实操,彻底搞懂HTTP协议的工作流程,为后续网络编程和接口开发打下扎实基础。


一、 HTTP协议

HTTP(超文本传输协议)是互联网的基础协议,用于客户端(如浏览器)和服务器之间的通信。

🐾 超文本:

  • 视频、音频、图片、图标、HTML文件等等

🐾 核心特点:

  • 请求-响应模型: 客户端发起请求,服务器返回响应。
  • 无状态: 默认不记住之前的请求(可通过Cookie等技术解决)。
  • 明文传输: 数据不加密,因此存在安全风险

1. HTTP的请求与响应格式

1.1 HTTP请求

在这里插入图片描述

🐾 首行:

  • [方法]+[url]+[版本]

🐾 Header:

  • 请求的属性, 冒号分割的键值对;
  • 每组属性之间使用\r \n分隔;遇到空⾏表示 Header部分结束

🐾 Body:

  • 空⾏后⾯的内容都是Body. Body允许为空字符串.
  • 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度

在这里插入图片描述


🐾 编写HTTP请求的代码 - 验证http请求

  • 需要现场基于历史代码,先架构处一个基本的HTTP服务器,然后用浏览器进⾏验证
  • 需要验证前端内容,直接AI即可

1.2 HTTP响应

在这里插入图片描述

🐾 ⾸⾏:

  • [版本号] + [状态码] + [状态码解释

🐾 Header:

  • 请求的属性, 冒号分割的键值对;
  • 每组属性之间使用 \r \n分隔;遇到空⾏表示Header部分结束

🐾 Body:

  • 空⾏后⾯的内容都是Body. Body允许为空字符串.
  • 如果Body存在, 则在Header中会有一个Content-Length属性来标识Body的长度;
  • 如果服务器返回了一个html⻚⾯, 那么html⻚⾯内容就是在body中.

在这里插入图片描述


1.3 基本的应答格式

在这里插入图片描述


1.4 HTTP的方法

在这里插入图片描述

GET方法(重点)
  • 用途:用于请求URL指定的资源。
  • 示例: GET /index.html HTTP/1.1
  • 特性:指定资源经服务器端解析后返回响应内容。

🐾 form表单:

https://www.runoob.com/html/html-forms.html

要通过历史写的http服务器,验证GET方法,这⾥需要了解一下FORM表单的问题
这⾥就要引入web根目录,⽂件读取的基本操作了

std::string GetFileContentHelper(const std::string& path)
{
	// 一份简单的读取⼆进制⽂件的代码
	std::ifstream in(path, std::ios::binary);
	if (!in.is_open())
		return "";
	in.seekg(0, in.end);
	int filesize = in.tellg();
	in.seekg(0, in.beg);
	std::string content;
	content.resize(filesize);
	in.read((char*)content.c_str(), filesize);
	// std::vector<char> content(filesize);
	// in.read(content.data(), filesize);
	in.close();
	return content;
}

POST方法(重点)
  • 用途:用于传输实体的主体,通常用于提交表单数据。
  • 示例: POST /submit.cgi HTTP/1.1
  • 特性:可以发送大量的数据给服务器,并且数据包含在请求体中。

🐾 form表单:

https://www.runoob.com/html/html-forms.html


PUT方法(不常用)
  • 用途:用于传输⽂件,将请求报⽂主体中的⽂件保存到请求URL指定的位置。
  • 示例: PUT /example.html HTTP/1.1
  • 特性:不太常用,但在某些情况下,如RESTful API中,用于更新资源。

HEAD方法
  • 用途:与GET方法类似,但不返回报⽂主体部分,仅返回响应头。
  • 示例: HEAD /index.html HTTP/1.1
  • 特性:用于确认URL的有效性及资源更新的⽇期时间等。
// curl -i 显示
$ curl - i www.baidu.com
HTTP / 1.1 200 OK
Accept - Ranges: bytes
Cache - Control : private, no - cache, no - store, proxy - revalidate, no - transform
Connection : keep - alive
Content - Length : 2381
Content - Type : text / html
Date : Sun, 16 Jun 2024 08 : 38 : 04 GMT
Etag : "588604dc-94d"
Last - Modified : Mon, 23 Jan 2017 13 : 27 : 56 GMT
Pragma : no - cache
Server : bfe / 1.0.8.18
Set - Cookie : BDORZ = 27315; max - age = 86400; domain = .baidu.com; path = /
<!DOCTYPE html>
...
// 使用head方法,只会返回响应头
$ curl --head www.baidu.com
HTTP / 1.1 200 OK
Accept - Ranges: bytes
Cache - Control : private, no - cache, no - store, proxy - revalidate, no - transform
Connection : keep - alive
Content - Length : 277
Content - Type : text / html
Date : Sun, 16 Jun 2024 08 : 43 : 38 GMT
Etag : "575e1f71-115"
Last - Modified : Mon, 13 Jun 2016 02 : 50 : 25 GMT
Pragma : no - cache
Server : bfe / 1.0.8.18

DELETE方法(不常用)
  • 用途:用于删除⽂件,是PUT的相反方法。
  • 示例: DELETE /example.html HTTP/1.1
  • 特性:按请求URL删除指定的资源。

OPTION方法
  • 用途:用于查询针对请求URL指定的资源⽀持的方法。
  • 示例: OPTIONS * HTTP/1.1
  • 特性:返回允许的方法,如GET、POST等。

🐾 不支持的效果

// 搭建一个nginx用来测试
// sudo apt install nginx
// sudo nginx -- 开启
// ps ajx | grep nginx -- 查看
// sudo nginx -s stop -- 停⽌服务
$ sudo nginx - s stop
$ ps ajx | grep nginx
2944845 2945390 2945389 2944845 pts / 1 2945389 S + 1002 0:00 grep --
color = auto nginx
$ sudo nginx
$ ps axj | grep nginx
			1 2945393 2945393 2945393 ? -1 Ss 0 0 : 00 nginx :
master process nginx
2945393 2945394 2945393 2945393 ? -1 S 33 0 : 00 nginx :
worker process
2945393 2945395 2945393 2945393 ? -1 S 33 0 : 00 nginx :
worker process
2944845 2945397 2945396 2944845 pts / 1 2945396 S + 1002 0 : 00 grep --
color = auto nginx

// -X(大x) 指明方法
$ curl - X OPTIONS - i http ://127.0.0.1/
HTTP / 1.1 405 Not Allowed
Server : nginx / 1.18.0 (Ubuntu)
Date : Sun, 16 Jun 2024 08 : 48 : 22 GMT
Content - Type : text / html
Content - Length : 166
Connection : keep - alive
<html>
<head> < title>405 Not Allowed< / title>< / head>
<body>
<center> < h1>405 Not Allowed< / h1>< / center>
<hr><center>nginx / 1.18.0 (Ubuntu) < / center >
< / body>
< / html>

🐾 支持的效果

HTTP/1.1 200 OK
Allow: GET, HEAD, POST, OPTIONS
Content-Type: text/plain
Content-Length: 0
Server: nginx/1.18.0 (Ubuntu)
Date: Sun, 16 Jun 2024 09:04:44 GMT
Access-Control-Allow-Origin: *
Access-Control-Allow-Methods: GET, POST, OPTIONS
Access-Control-Allow-Headers: Content-Type, Authorization
// 注意:这⾥没有响应体,因为Content-Length为0

1.5 HTTP的状态码

在这里插入图片描述


🐾 最常见的状态码, 比如

  • 200(OK)
  • 404(Not Found)
  • 403(Forbidden)
  • 302(Redirect, 重定向)
  • 504(BadGateway)
状态码 含义 应用详情
100 Continue 上传文件时,服务器高速客户端可以继续上传
200 OK 访问网站网页,服务器返回网页内容
201 Created 发布新文章,服务器返回文章创建成功的信息
204 NoContent 删除文章后,服务器返回“无内容”表示操作成功
301 Moved Perimanently 网站换域名后,自动跳转到新域名;搜索引擎更新网站时使用
302 Found或See Other 用户名登录成功后,重定向到用户首页
304 Not Modified 浏览器缓存机制,对未修改的资源返回304状态码
400 Bad Rrequest 填写表单时,格式不正确导致提交失败
401 Unauthorized 访问需要登录的页面时,未登录或认证失败
403 Forbidden 尝试访问你没有权限查看的页面
404 Not Found 访问不存在的网页链接
500 internal Server Error 服务器崩溃或数据库错误导致页面无法加载
502 Bad Geteway 使用代理服务器时,代理服务器无法从上游服务器获有效响应
503 Service Unavailable 服务器在维护或过载,暂时无法处理请求

🐾 以下是仅包含重定向相关状态码的表格

状态码 含义是否为临时重定向 应用样例
301 Moved Peimanently 否(永久重定向)
302 Found或See Other 是(临时重定向)
307 Temporay Redirect 是(临时重定向)
308 Permanent Redirect 否(永久定居)

🐾 关于重定向的验证,以301为代表

HTTP状态码301(永久重定向)和302(临时重定向)都依赖Location选项


🐾 以下是关于两者依赖Location选项的详细说明:

HTTP状态码301(永久重定向)
  • 当服务器返回HTTP 301状态码时,表示请求的资源已经被永久移动到新的位置。
  • 在这种情况下,服务器会在响应中添加一个Location头部,用于指定资源的新位置。这个Location头部包含了新的URL地址,浏览器会自动重定向到该地址。
  • 例如,在HTTP响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 301 Moved Permanently\r\n
Location: https://www.new-url.com\r\n

HTTP状态码302(临时重定向)
  • 重点内容 当服务器返回HTTP 302状态码时,表示请求的资源临时被移动到新的位置。
  • 同样地,服务器也会在响应中添加一个Location头部来指定资源的新位置。浏览器会暂时使用新的URL进⾏后续的请求,但不会缓存这个重定向。
  • 例如,在HTTP响应中,可能会看到类似于以下的头部信息:
HTTP/1.1 302 Found\r\n
Location: https://www.new-url.com\r\n

总结: ⽆论是HTTP 301还是HTTP 302重定向,都需要依赖Location选项来指定资源的新位置。这个Location选项是一个标准的HTTP响应头部,用于告诉浏览器应该将请求重定向到哪个新的URL地址。


1.6 HTTP常见Header

  • Content-Type: 数据类型(text/html等)
  • Content-Length: Body的长度
  • Host: 客户端告知服务器, 所请求的资源是在哪个主机的哪个端口上;
  • User-Agent: 声明用户的操作系统和浏览器版本信息;
  • Referer: 当前⻚⾯是从哪个⻚⾯跳转过来的;
  • Location: 搭配3xx状态码使用, 告诉客户端接下来要去哪⾥访问;
  • Cookie: 用于在客户端存储少量信息. 通常用于实现会话(session)的功能;

关于connection报头

重点内容 HTTP中的 Connection 字段是HTTP报⽂头的一部分,它主要用于控制和管理客户端与服务器之间的连接状态

🐾 核心作用

  • 管理持久连接: Connection 字段还用于管理持久连接(也称为长连接)。持久连接允许客户端和服务器在请求/响应完成后不立即关闭TCP连接,以便在同一个连接上发送多个请求和接收多个响应。持久连接(长连接)
    - HTTP/1.1:在HTTP/1.1协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
  • HTTP/1.0:在HTTP/1.0协议中,默认连接是非持久的。如果希望在HTTP/1.0上实现持久连接,需要在请求头中显式设置 Connection: keep-alive 。

🐾 语法格式

  • Connection: keep-alive :表示希望保持连接以复用TCP连接。
  • Connection: close :表示请求/响应完成后,应该关闭TCP连接。

User-Agent⾥的历史故事

字段名 含义 样例
Accept 客户端可接受的响应内容类型 Accept:text/html,application/xhtml+xml,application/xml;q=0.9,image/webp,image/apng,/;q=0.8
Accept-Encoding 客户端⽀持的数据压缩格式 Accept-Encoding: gzip, deflate, br
Accept-Language 客户端可接受的语⾔类型 Accept-Language: zh-CN,zh;q=0.9,en;q=0.8
Host 请求的主机名和端口号 Host: www.example.com:8080
User-Agent 客户端的软件环境信息 User-Agent: Mozilla/5.0 (Windows NT10.0;Win64;x64)AppleWebKit/537.36 (KHTML, likeGecko) Chrome/91.0.4472.124Safari/537.36
Cookie 客户端发送给服务器的HTTP cookie信息 Cookie: session_id=abcdefg12345; user_id=123
Referer 请求的来源URL Referer:http://www.example.com/previous_page.html
Content-Type 实体主体的媒体类型 Content-Type: application/x-www-formurlencoded (对于表单提交) 或 Content-Type:application/json (对于JSON数据)
Content-Length 实体主体的字节大小 Content-Length: 150
Authorization 认证信息,如用户名和密码 Authorization: BasicQWxhZGRpbjpvcGVuIHNlc2FtZQ== (Base64编码后的用户名:密码)
Cache-Control 缓存控制指令 请求时: Cache-Control: no-cache 或 CacheControl: max-age=3600 ;响应时: CacheControl: public, max-age=3600
Connection 请求完后是关闭还是保持连接 Connection: keep-alive 或 Connection: close
Date 请求或响应的⽇期和时间 Date: Wed, 21 Oct 2023 07:28:00 GMT
erver 服务器类型 Server: Apache/2.4.41 (Unix)
Last-Modified 资源的最后修改时间 Last-Modified: Wed, 21 Oct 2023 07:20:00 GMT
ETag 资源的唯一标识符,用于缓存 ETag: “3f80f-1b6-5f4e2512a4100”
Expires 响应过期的⽇期和时间 Expires: Wed, 21 Oct 2023 08:28:00 GMT

🐶 🐾 ✨ 🐾 🐶


2. 最简单的HTTP服务器

实现一个最简单的HTTP服务器, 只在网⻚上输出 "hello world"; 只要我们按照HTTP协议的要求构造数据, 就很容易能做到;

#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdio.h>
#include <string.h>
#include <stdlib.h>
void Usage() {
	printf("usage: ./server [ip] [port]\n");
}
int main(int argc, char* argv[]) {
	if (argc != 3) {
		Usage();
		return 1;
	}
	int fd = socket(AF_INET, SOCK_STREAM, 0);
	if (fd < 0) {
		perror("socket");
		return 1;
	}
	
		struct sockaddr_in addr;
	addr.sin_family = AF_INET;
	addr.sin_addr.s_addr = inet_addr(argv[1]);
	addr.sin_port = htons(atoi(argv[2]));
	int ret = bind(fd, (struct sockaddr*)&addr, sizeof(addr));
	if (ret < 0) {
		perror("bind");
		return 1;
	}
	ret = listen(fd, 10);
	if (ret < 0) {
		perror("listen");
		return 1;
	}
	for (;;) {
		struct sockaddr_in client_addr;
		socklen_t len;
		int client_fd = accept(fd, (struct sockaddr*)&client_addr, &len);
		if (client_fd < 0) {
			perror("accept");
			continue;
		}
		char input_buf[1024 * 10] = { 0 }; // 用一个⾜够大的缓冲区直接把数据读完.
		ssize_t read_size = read(client_fd, input_buf, sizeof(input_buf) - 1);
		if (read_size < 0) {
			return 1;
		}
		printf("[Request] %s", input_buf);
		char buf[1024] = { 0 };
		const char* hello = "<h1>hello world</h1>";
		sprintf(buf, "HTTP/1.0 200 OK\nContent-Length:%lu\n\n%s", strlen(hello),
			hello);
		write(client_fd, buf, strlen(buf));
	}
	return 0;
}

编译, 启动服务. 在浏览器中输入 http://[ip]:[port], 就能看到显示的结果 “Hello World”

🐶 🐾 ✨ 🐾 🐶


3.附录

HTTP历史及版本核心技术与时代背景

HTTP(Hypertext Transfer Protocol,超⽂本传输协议)作为互联网中浏览器和服务器间通信的基⽯,经历了从简单到复杂、从单一到多样的发展过程。

🐾 以下将按照时间顺序,介绍HTTP的主要版本、核心技术及其对应的时代背景。


HTTP/0.9

🐾 核心技术:

• 仅⽀持GET请求方法。

• 仅⽀持纯⽂本传输,主要是HTML格式。

• ⽆请求和响应头信息。

🐾 时代背景:

• 1991年,HTTP/0.9版本作为HTTP协议的最初版本,用于传输基本的超⽂本HTML内容。

• 当时的互联网还处于起步阶段,网⻚内容相对简单,主要以⽂本为主。


HTTP/1.0

🐾 核心技术:

  • 引入POST和HEAD请求方法。
  • 请求和响应头信息,⽀持多种数据格式(MIME)。
  • ⽀持缓存(cache)。
  • 状态码(status code)、多字符集⽀持等。

🐾 时代背景:

  • 1996年,随着互联网的快速发展,网⻚内容逐渐丰富,HTTP/1.0版本应运而⽣。

  • 为了满⾜⽇益增长的网络应用需求,HTTP/1.0增加了更多的功能和灵活性。

  • 然而,HTTP/1.0的⼯作方式是每次TCP连接只能发送一个请求,性能上存在一定局限。


HTTP/1.1

🐾 核心技术:

  • 引入持久连接(persistent connection),⽀持管道化(pipelining)。

  • 允许在单个TCP连接上进⾏多个请求和响应,提⾼了性能。

  • 引入分块传输编码(chunked transfer encoding)。

  • ⽀持Host头,允许在一个IP地址上部署多个Web站点。

🐾 时代背景:

  • 1999年,随着网⻚加载的外部资源越来越多,HTTP/1.0的性能问题愈发突出。
  • HTTP/1.1通过引入持久连接和管道化等技术,有效提⾼了数据传输效率。
  • 同时,互联网应用开始呈现出多元化、复杂化的趋势,HTTP/1.1的出现满⾜了这些需求。

HTTP/2.0

🐾 核心技术:

  • 多路复用(multiplexing),一个TCP连接允许多个HTTP请求。
  • ⼆进制帧格式(binary framing),优化数据传输。
  • 头部压缩(header compression),减少传输开销。
  • 服务器推送(server push),提前发送资源到客户端。

🐾 时代背景:

  • 2015年,随着移动互联网的兴起和云计算技术的发展,网络应用对性能的要求越来越⾼。
  • HTTP/2.0通过多路复用、⼆进制帧格式等技术,显著提⾼了数据传输效率和网络性能。
  • 同时,HTTP/2.0还⽀持加密传输(HTTPS),提⾼了数据传输的安全性。

HTTP/3.0

🐾 核心技术:

  • 使用QUIC协议替代TCP协议,基于UDP构建的多路复用传输协议。
  • 减少了TCP三次握⼿及TLS握⼿时间,提⾼了连接建立速度。
  • 解决了TCP中的线头阻塞问题,提⾼了数据传输效率。

🐾 时代背景:

  • 2022年,随着5G、物联网等技术的快速发展,网络应用对实时性、可靠性的要求越来越⾼。
  • HTTP/3.0通过使用QUIC协议,提⾼了连接建立速度和数据传输效率,满⾜了这些需求。
  • 同时,HTTP/3.0还⽀持加密传输(HTTPS),保证了数据传输的安全性

🐶 🐾 ✨ 🐾 🐶


举爪爪求关注

谢谢你看到这里呀

如果喜欢这篇内容,点个关注,下次更新不迷路✨

👍 点赞 ⭐ 收藏 💬 评论

Logo

openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构

更多推荐