【Linux】一文搞懂HTTP协议:概念、报文格式与极简服务器实现
本文为HTTP协议学习笔记,主要讲解HTTP协议的基础概念、请求报文与响应报文的格式解析,并结合Socket编程实现了一个最简单的HTTP服务器,帮助初学者从理论到实操理解HTTP协议的工作原理,搭建应用层网络编程的基础认知。
📌 相关文章推荐
很高兴你点开这篇文章✨
这里会持续更新我喜欢的内容,关注我,一起慢慢变好呀
👍 点赞 ⭐ 收藏 💬 评论
前言
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连接。
| 字段名 | 含义 | 样例 |
|---|---|---|
| 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),保证了数据传输的安全性
🐶 🐾 ✨ 🐾 🐶
谢谢你看到这里呀
如果喜欢这篇内容,点个关注,下次更新不迷路✨
👍 点赞 ⭐ 收藏 💬 评论
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐









所有评论(0)