http基础

Http基本知识

HTTP(HyperText Transfer Protocol 超⽂本传输协议)是⼀种基于TCP/IP协议的应⽤层通信协议,主要⽤于万维⽹(WWW)中客⼾端与服务器之间的通信。它是互联⽹上数据交换的基础,允许⽤⼾从WWW服务器传输超⽂本⽂件到本地浏览器。HTTP的主要功能是定义了客⼾端如何向服务器请求⽹⻚内容以及服务器如何响应这些请求。

HTTP协议规定的内容

HTTP协议规定的内容:(理解)

  1. 请求/响应模型: HTTP采⽤客⼾端-服务器模式,客⼾端(如Web浏览器)发送HTTP请求给服务器(如Web服务器),服务器处理请求并返回HTTP响应。
  2. 请求⽅法: 规定了多种请求⽅法,如GET、POST、PUT、DELETE等,每种⽅法对应不同的操作语义。例如,GET⽤于获取资源,POST⽤于提交数据创建新资源或更新现有资源。
  3. 状态码: 服务器在响应中会包含⼀个三位数字的状态码,如200表⽰成功,404表⽰未找到资源,500表⽰服务器内部错误等,⽤以告知客⼾端请求的处理结果。
  4. 消息头: 请求和响应中都包含了多个消息头字段,如Host、Content-Type、Cookie、Authorization等,⽤于描述请求属性、响应内容类型、客⼾端和服务端的附加信息等。
  5. 消息体: 请求和响应可以携带⼀个可选的消息体,通常承载着要发送的数据或响应的具体内容,如HTML⽂档、JSON数据、图⽚⽂件等。

HTTP协议特点

  1. 请求/响应模型: HTTP采⽤客⼾端/服务器模式⼯作,客⼾端(如Web浏览器)发起HTTP请求给服务器(如Web服务器),服务器接收并处理请求后返回HTTP响应。
  2. ⽆状态: 默认情况下,HTTP协议是⽆状态的,这意味着服务器不会保留两次请求之间的任何上下⽂信息。为了实现状态管理,可以使⽤Cookie、Session等技术。

Cookie和Session

是⼀种在客⼾端(通常是Web浏览器)存储⽤⼾信息的机制,服务器通过HTTP响应头Set-Cookie来设置Cookie。浏览器收到后将这些信息存储在本地,并在后续请求中⾃动附带到HTTP请求头Cookie中发送给服务器。Cookie可以存储⽤⼾⾝份验证凭据、个性化设置、购物⻋内容等少量数据。

特点:

  1. 客⼾端存储:Cookie是保存在客⼾端浏览器上的⽂本⽂件,容易被⽤⼾查看和修改。
  2. 有限容量:单个Cookie⼤⼩限制通常为4KB左右,且每个域名下的Cookie数量也有限制。
  3. 安全性较低:由于存储在客⼾端,敏感信息不宜直接存储在Cookie中,需加密处理或使⽤
    HttpOnly属性防⽌JavaScript读取以提⾼安全性。
  4. ⽣命周期可配置:Cookie有⽣命周期,可以通过Expires或Max-Age属性指定过期时间,否则默
    认为会话级Cookie(浏览器关闭时删除)。

Cookie的好处:

  1. 持久化⽤⼾状态:Cookie允许在客⼾端存储⼀些⼩量且⾮敏感的数据,如⽤⼾的语⾔偏好、主题设置等,使得⽤⼾在下次访问时能保持⼀致的体验。
  2. 简化登录过程:通过将⾝份验证凭证(通常为加密过的Token)保存在Cookie中,实现“记住我”功能,使⽤⼾⽆需每次访问都重新登录。
  3. 追踪分析:⽹站和第三⽅服务可以使⽤Cookie进⾏匿名或基于同意的⽤⼾⾏为追踪和分析,以优化⽤⼾体验和服务质量。

Session

Session 是⼀种服务器端技术,⽤于维护⽤⼾状态。当⽤⼾访问⽹站并进⾏登录等操作时,服务器创建⼀个与该⽤⼾相关的Session对象并在内存或数据库中存储⽤⼾的会话信息。服务器会给客⼾端分配⼀个唯⼀的Session ID,并将其作为Cookie或URL重写的⽅式传递给客⼾端。客⼾端在后续请求中携带Session ID,服务器通过识别ID获取对应的Session数据,实现⽤⼾状态跟踪。

特点:

  1. 服务器端存储:Session数据存储在服务器端,相⽐Cookie更安全,不易被篡改。
  2. 数据量⽆严格限制:Session可以在服务器端存储较⼤量的数据,受限于服务器资源。
  3. ⽣命周期管理:服务器可以根据需要管理Session的⽣命周期,如设定固定的有效期或者检测⽤⼾活动情况⾃动延⻓有效期。
  4. 跨域共享问题:由于Session依赖于服务器,在分布式服务器架构下可能需要额外的技术⼿段(如Session复制、集中式Session存储)来实现跨多个服务器节点的Session共享。

Session的好处:

  1. 安全存储:由于Session数据存储在服务器端,相⽐Cookie更安全,不易被窃取或篡改。可以放⼼地在其中存储⽤⼾的登录状态和其他敏感信息。
  2. 较⼤容量:不受限于单个Cookie⼤⼩限制,Session可以存储更多类型和更⼤规模的数据。
  3. 灵活管理:服务器可以根据需要管理和控制Session的⽣命周期,例如⾃动清除⻓时间⽆操作的Session,从⽽保护系统资源并提升安全性。
  4. 跨域共享:虽然分布式架构下需要额外处理,但理论上Session可以通过设计⽀持跨多个服务器节点共享,实现在集群环境下的⽆缝会话跟踪。

总结

总结来说,Cookie和Session都是为了维持⽤⼾状态⽽在客⼾端和服务端之间建⽴的⼀种关联制,但它们在存储位置、安全性、数据量以及管理⽅式上存在显著差异。在实际应⽤中,⼆者常结合使⽤,Cookie⽤于传输Session ID,⽽Session则⽤来存储真正敏感和⼤量⽤⼾会话数据。

MIME类型

HTTP不仅可以传输HTML⽂档,还可以传输图⽚、视频、⾳频等各种格式的数据,通过Content-Type头字段来标识资源的MIME类型。
MIME类型(Multipurpose Internet Mail Extensions)是⼀种标准,⽤于描述互联⽹上传输的数据内容的格式和类型。

  • text/html :表⽰资源是HTML⽂档。
  • image/jpeg :表⽰资源是JPEG格式的图⽚。
  • application/json :表⽰资源是以JSON格式编码的数据。
  • video/mp4 :表⽰资源是⼀个MP4格式的视频⽂件。

基本HTTP交互流程:

  • 客⼾端构造⼀个HTTP请求报⽂,其中包括请求⾏(Method, URL, Version)、请求头部(Header)、空⾏及可选的消息体。
  • 服务器收到请求后,解析请求报⽂,根据请求⽅法和URL找到对应的资源,并⽣成包含状态码、响应头部、空⾏及可选的消息体在内的HTTP响应报⽂。
  • 客⼾端接收到响应报⽂后,解析并根据响应的内容类型展⽰相应的数据(如渲染HTML⻚⾯、下载⽂件等)。

不同版本的HTTP协议

HTTP 1.0:

  • 连接管理:每个请求都会创建⼀个新的TCP连接,完成请求后⽴即关闭连接,即⾮持久连接。这导致了“请求-响应-关闭”的模式,对资源密集型⽹站来说效率低下,因为建⽴TCP连接本⾝就需要时间(三次握⼿)。

HTTP 1.1:

  • 发布于1999年,作为HTTP 1.0的升级版,它改进了许多不⾜之处,现在⼤部分应⽤实际上使⽤的是HTTP 1.1⽽⾮1.0。(最多不是2.0是因为有的古⽼的服务器懒得更新)
  • 持久连接:默认采⽤持续连接(Keep-Alive),⼀个TCP连接可以服务于多个请求,减少连接建⽴与释放的成本。

HTTP/2:

  • 多路复⽤:通过单⼀TCP连接并⾏发送多个请求和响应,解决了队头阻塞问题,⼤幅提升了⻚⾯加载速度。
  • 总的来说,HTTP 1.0到HTTP/2的演进过程反映了互联⽹技术不断发展以及⽤⼾对⽹⻚加载速度需求的提⾼。

HTTP和HTTPS

HTTP(HyperText Transfer Protocol)和HTTPS(Hypertext Transfer Protocol Secure)都是⽤于在互联⽹上传输数据的应⽤层协议,但它们之间存在显著区别:

  1. 安全传输:
  • HTTP:HTTP是⼀种⽆状态、明⽂传输的协议,不提供任何加密措施。这意味着通过HTTP传输的数据是未加密的,容易被监听、篡改或伪造。
  • HTTPS:HTTPS是在HTTP的基础上添加了SSL/TLS协议层,对传输数据进⾏加密,从⽽保证通信过程中的安全性。它可以保护⽤⼾信息如账号密码、交易数据等不被第三⽅窃取。
  1. ⾝份验证:
  • HTTP:HTTP协议本⾝⽆法验证服务器的⾝份,任何⼈都可以架设⼀个声称是某个⽹站的服务
    器,这可能导致中间⼈攻击或者钓⻥⽹站欺诈。
  • HTTPS:使⽤了SSL/TLS证书,可以实现服务器的⾝份验证。浏览器会检查服务器提供的证书是否由受信任的CA机构颁发,并确认其域名与证书上的域名⼀致,从⽽确保⽤⼾访问的是真实可靠的服务器。
  1. 信任标志:
  1. 性能影响:
  • HTTP:由于不需要进⾏加密解密操作,理论上HTTP在数据传输速度上⽐HTTPS稍快⼀些。
  • HTTPS:虽然增加了加密和认证环节,导致⼀定的性能开销,但随着硬件计算能⼒提升和技术优化,这个差距正在逐渐缩⼩。⽽且很多现代浏览器⽀持HTTP/2、HTTP/3以及TLS 1.3等新标准,在⼀定程度上弥补了HTTPS的性能损失。
  1. 综上所述,HTTPS提供了更⾼级别的数据加密和服务器⾝份验证功能,是现代Web应⽤和服务推荐使⽤的传输协议,尤其是在涉及敏感信息交换的情况下。

请求与相应结构

HTTP请求结构

HTTP请求是客⼾端向服务器发送的消息,⽤于请求资源或提交数据。每个HTTP请求包含以下部分:

  1. 请求⾏(Request Line):请求⾏包含了HTTP⽅法、URI(统⼀资源标识符)和HTTP版本。它的格式通常如下:
    1
    HTTP⽅法 URI HTTP版本
    例如,以下请求⾏表⽰客⼾端请求获取 /index.html 资源,使⽤HTTP 1.1协议:
    1
    1 GET /index.html HTTP/1.1
  • HTTP⽅法(Method):指定了客⼾端的请求类型,常⻅的⽅法包括GET、POST、PUT、DELETE等。
  • URI(Uniform Resource Identifier):标识了所请求的资源的位置和名称。
  • HTTP版本:指定了所使⽤的HTTP协议版本,例如HTTP/1.1。
  1. 请求头(Request Headers):请求头包含了附加的信息,⽤于描述客⼾端的环境和请求体的信息。请求头以⼀⾏⼀⾏的键值对形式出现,每⾏由字段名和字段值组成,中间使⽤冒号(:)分隔。以下是⼀些常⻅的请求头⽰例:
    1
    2
    Content-Type: application/json
    User-Agent: Mihoyo/5.0
  • Content-Type 字段指定了请求体的内容类型,这对于服务器解析请求体⾮常重要,例如,它可以表⽰请求体是JSON格式数据。
  • User-Agent 字段通常包含了客⼾端的⽤⼾代理信息,⽤于标识请求的来源,例如浏览器类型和版本。
  1. 请求体(Request Body,可选):请求体仅在⼀些HTTP⽅法中使⽤,主要⽤于传输数据。通常,GET请求没有请求体,⽽POST请求则可能包含表单数据、JSON数据或⽂件内容等。请求体的格式和内容取决于请求头中的 Content-Type 字段。

GET与POST请求

GET请求:

  • ⽤于请求服务器上的资源。
  • 参数通常附加在URI后,如 /search?q= yuanshen,表⽰请求搜索关键为”yuanshen”的结果。
    1
    2
    3
    GET /search?q=yuanshen HTTP/1.1
    Host: www.example.com
    User-Agent: Mihoyo/5.0
  • HTTP⽅法是GET,表⽰请求获取资源。
  • URI是 /search?q=openai ,包含了参数 q ,其值为 openai ,表⽰搜索关键词为”openai”的结果。
  • Host字段指定了服务器的主机名。
  • User-Agent字段表⽰请求来⾃Mihoyo浏览器。

POST请求:

  • ⽤于向服务器提交数据,如表单提交。
  • 数据放置在请求体中,不出现在URI中。
    1
    2
    3
    4
    5
    6
    POST /submit-form HTTP/1.1
    Host: www.example.com
    User-Agent: Mihoyo/5.0
    Content-Type: application/x-www-form-urlencoded
    Content-Length: 21
    username=johndoe&password=12345
  • HTTP⽅法是POST,表⽰请求提交数据到服务器。
  • URI是 /submit-form ,不包含参数。
  • Host字段指定了服务器的主机名。
  • User-Agent字段表⽰请求来⾃Mihoyo浏览器。
  • Content-Type字段指定了请求体的内容类型为 application/x-www-formurlencoded ,表⽰数据以表单形式编码。
  • Content-Length字段指定了请求体的⻓度为21个字节。
  • 请求体中包含了表单数据,其中 username 字段的值为 johndoe , password 字段的值为12345 。

Http解析

解析HTTP请求的⽬的是获取请求中的关键信息,如⽅法、URI和请求体内容。

  • 解析⽅法和URI:
    • 从请求⾏中提取HTTP⽅法(GET、POST等)和请求的URI。
    • 例如,请求⾏ GET /index.html HTTP/1.1 中,⽅法为GET,URI为 /index.html 。
  • 处理POST请求体中的数据:
    • 如果请求⽅法是POST,从请求体中提取提交的数据。
    • 例如,表单提交的POST请求中,请求体可能包含键值对形式的数据。

实例:解析HTTP请求

假设接收到的HTTP请求内容为:

1
2
GET /index.html HTTP/1.1
Host: www.example.comUser-Agent: Mihoyo/5.0
  • 解析请求:
    • 请求⾏为 GET /index.html HTTP/1.1 ,表⽰这是⼀个GET请求,请求的资源为 /index.html 。
    • 请求头包含 Host: www.example.com 和 User-Agent: Mihoyo/5.0 ,分别表⽰请求的服务器地址和客⼾端信息。
    • 由于是GET请求,因此没有请求体。

HTTP响应

HTTP响应结构

  1. 状态⾏(Status Line)
    状态⾏是HTTP响应的第⼀部分,包含了HTTP版本、状态码和状态消息。其格式如下:
    1 HTTP版本 状态码 状态消息
    例如:
    1
    HTTP/1.1 200 OK
  • HTTP版本:标识了服务器返回响应时所使⽤的HTTP协议版本。
  • 状态码:三位数字代码,指⽰请求处理的结果。如 200 表⽰成功, 404 表⽰未找到资源, 500表⽰服务器内部错误等。
  • 状态消息:对状态码的简短描述,如”OK”、”Not Found”等。
  1. 响应头(Response Headers)
    响应头提供了关于响应的附加信息,以键值对的形式列出。⽰例:
    1
    2
    3
    Content-Type: text/html; charset=UTF-8
    Cache-Control: max-age=3600
    Server: Apache/2.4.41
  • Content-Type:指定了响应体的内容类型及编码。
  • Cache-Control:指导客⼾端如何缓存响应内容。
  • Server:标明处理请求的服务器及其软件版本。
  1. 响应体(Response Body,可选)
    响应体包含了服务器向客⼾端发送的实际数据,根据响应头中的Content-Type字段解析。响应体可以是HTML⽂档、JSON数据、图⽚或其他任何类型的数据。

GET与POST响应⽰例

GET响应⽰例

1
2
3
4
5
6
7
GET /index.html HTTP/1.1
Host: www.example.com
User-Agent: Mihoyo/5.0
HTTP/1.1 200 OK
Content-Type: text/html; charset=UTF-8
Content-Length: 1024
<!DOCTYPE html...

在上述例⼦中,服务器成功返回了GET请求的资源,并通过响应体返回了⼀个HTML⻚⾯。
POST响应⽰例

1
2
3
4
5
6
7
8
9
10
11
POST /submit-form HTTP/1.1
Host: www.example.com
User-Agent: Mihoyo/5.0
Content-Type: application/x-www-form-urlencoded
Content-Length: 21
username=johndoe&password=12345
HTTP/1.1 201 Created
Location: /profile/johndoe
Content-Type: application/json
Content-Length: 56
{"status": "success", "message": "Form submitted"}

在这个例⼦中,服务器接收到并成功处理POST请求后,返回⼀个201状态码,表⽰新资源已创建,并通过响应体提供JSON格式的操作结果。
响应解析

  • 解析状态码和状态消息:从状态⾏中提取相关信息,了解请求执⾏的成功与否。
  • 处理响应体:根据响应头中的Content-Type字段确定响应体格式,并从中获取具体数据进⾏进⼀步处理。例如,如果是JSON格式,则将其解析为JSON对象。

代码

代码逻辑

  1. 解析HTTP请求
  • 从接收到的HTTP请求中解析出请求⽅法和URI。
  • 对于POST,解析请求体。
  1. 处理GET和POST请求
  • GET:提取URI参数,进⾏处理。
  • POST:获取请求体数据。
  1. ⽣成并返回响应
  • 根据处理结果,⽣成HTTP响应。
  • 设置响应头和响应体,返回客⼾端。
    实战⽰例:简单HTTP服务器
  1. 服务器初始化
  • 创建TCP socket,绑定端⼝,监听。
  1. 解析HTTP请求
  • 接收客⼾端HTTP请求。
  • 使⽤ parseHttpRequest 函数解析请求⽅法和URI。
  1. 处理请求
  • 根据URI调⽤相应处理函数。
  • 针对GET/POST执⾏逻辑。
  1. 发送响应
  • 构建HTTP响应,发送回客⼾端

具体代码

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
#include <iostream>
#include <map>
#include <functional>
#include <string>
#include <sys/socket.h>
#include <stdlib.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>

#define PORT 8080

// 请求处理函数类型定义
using RequestHandler = std::function<std::string(const std::string&)>;

// 分别为GET和POST请求设置路由表
std::map<std::string, RequestHandler> get_routes;
std::map<std::string, RequestHandler> post_routes;

// 初始化路由表
void setupRoutes() {
// GET请求处理
get_routes["/"] = [](const std::string& request) {
return "Hello, World!";
};
get_routes["/register"] = [](const std::string& request) {
// TODO: 实现用户注册逻辑
return "Please use POST to register";
};

get_routes["/login"] = [](const std::string& request) {
// TODO: 实现用户登录逻辑

return "Please use POST to login";
};

// POST请求处理
post_routes["/register"] = [](const std::string& request) {
// TODO: 实现用户注册逻辑
return "Register Success!";
};
post_routes["/login"] = [](const std::string& request) {
// TODO: 实现用户登录逻辑
return "Login Success!";
};

// TODO: 添加其他路径和处理函数
}

// 新增,解析HTTP请求
std::pair<std::string, std::string> parseHttpRequest(const std::string& request) {
// 找到第一个空格,确定HTTP方法的结束位置
size_t method_end = request.find(" ");
// 提取HTTP方法(如GET、POST)
std::string method = request.substr(0, method_end);

// 找到第二个空格,确定URI的结束位置
size_t uri_end = request.find(" ", method_end + 1);
// 提取URI(统一资源标识符)
std::string uri = request.substr(method_end + 1, uri_end - method_end - 1);

// 返回解析出的HTTP方法和URI
return {method, uri};
}

// 处理HTTP请求
std::string handleHttpRequest(const std::string& method, const std::string& uri, const std::string& body) {
// 检查GET请求和URI是否在路由表中
if (method == "GET" && get_routes.count(uri) > 0) {
// 根据URI调用相应的处理函数
return get_routes[uri](body);
}
// 检查POST请求和URI是否在路由表中
else if (method == "POST" && post_routes.count(uri) > 0) {
// 根据URI调用相应的处理函数
return post_routes[uri](body);
}
// 如果请求方法和URI不匹配任何路由,则返回404错误
else {
return "404 Not Found";
}
}


int main() {
int server_fd, new_socket;
struct sockaddr_in address;
int addrlen = sizeof(address);

// 创建socket
server_fd = socket(AF_INET, SOCK_STREAM, 0);

// 定义地址
address.sin_family = AF_INET;
address.sin_addr.s_addr = INADDR_ANY;
address.sin_port = htons(PORT);

// 绑定socket
bind(server_fd, (struct sockaddr *)&address, sizeof(address));

// 监听请求
listen(server_fd, 3);

// 设置路由
setupRoutes();

while (true) {
// 接受连接
new_socket = accept(server_fd, (struct sockaddr *)&address, (socklen_t*)&addrlen);

// 读取请求
char buffer[1024] = {0};
read(new_socket, buffer, 1024);
std::string request(buffer);

// 解析请求
auto [method, uri] = parseHttpRequest(request);

// 处理请求
std::string response_body = handleHttpRequest(method, uri, request);

// 发送响应
std::string response = "HTTP/1.1 200 OK\nContent-Type: text/plain\n\n" + response_body;
send(new_socket, response.c_str(), response.size(), 0);
// 关闭连接
close(new_socket);
}

return 0;
}