【Linux】网络编程:应用层协议—HTTP协议
目录
一、HTTP协议概念
HTTP协议的基本概念
HTTP的工作流程
二、认识URL
URL 的基本结构
URLencode与URLdecode
三、认识HTTP协议
四、HTTP请求格式
1. 请求行(Request Line)
2. 请求报头(Request Headers)
3. 空行(Blank Line)
4. 请求正文(Request Body)
示例完整HTTP请求
五、HTTP请求中常见的请求方法
六、GET和POST的作用与区别
GET请求:
POST请求:
区别:
七、HTTP响应格式
1. 状态行(Status Line)
2. 响应报头(Response Headers)
3. 空行(Blank Line)
4. 响应正文(Response Body)
示例完整HTTP响应
状态码详解
常见的状态码及解释
八、HTTP中的常见的请求报头属性
九、Cookie与Session
1. Cookie
1.1 基本概念
1.2 工作原理
1.3 特点
2. Session
2.1 基本概念
2.2 工作原理
2.3 特点
3. 对比与总结
4. 适用场景
5. 安全性和最佳实践
一、HTTP协议概念
HTTP(Hyper Text Transfer Protocol), 全称超文本传输协议,是用于从万维网(WWW:World Wide Web )服务器传输超文本到本地浏览器的传送协议。
HTTP 是一种应用层协议,是基于 TCP/IP 通信协议来传递数据的,其中 HTTP1.0、HTTP1.1、HTTP2.0 均为 TCP 实现,HTTP3.0 基于 UDP 实现。现主流使用 HTTP1.0 和 HTTP3.0。
协议: 为了使数据在网络上从源头到达目的,网络通信的参与方必须遵循相同的规则,这套规则称为协议,它最终体现为在网络上传输的数据包的格式。
【注意: 当我们访问一些网页时,是显示通过 HTTPS 来进行通信的,并且当下大多数的网页都是通过 HTTPS 来进行通信的,因为 HTTPS 在 HTTP 的基础上做了一个加密的工作。】
HTTP协议的基本概念
-
客户端-服务器模型:HTTP协议基于客户端-服务器模型。客户端(如浏览器)发起请求,服务器处理请求并返回响应。
-
无状态协议:HTTP是一个无状态协议,这意味着每个请求都是独立的,服务器不会保留以前的请求信息。为了保持状态,Web应用通常使用Cookie和Session等技术。
-
文本协议:HTTP协议使用纯文本格式进行通信,这使得它易于调试和理解。
HTTP的工作流程
一个典型的HTTP请求-响应周期如下:
-
建立TCP连接:客户端首先与服务器建立TCP连接。HTTP协议通常使用80端口(HTTP)或443端口(HTTPS)。
-
发送HTTP请求:客户端通过TCP连接发送HTTP请求。请求包括请求行、请求头和请求体(可选)。
-
处理请求:服务器接收到请求后,解析请求,执行相应的操作(如查询数据库、处理文件等),并生成响应。
-
返回HTTP响应:服务器将响应通过TCP连接发送回客户端。响应包括状态行、响应头和响应体。
-
关闭连接:请求和响应完成后,TCP连接通常会被关闭。HTTP/1.1引入了持久连接(Keep-Alive),允许在同一连接上进行多次请求和响应。
当我们在浏览器输入一个网址,此时浏览器就会给对应的服务器发送一个 HTTP 请求,对应的服务器收到这个请求之后,经过计算处理,就会返回一个 HTTP 响应。并且当我们访问一个网站时,可能涉及不止一次的 HTTP 请求和响应的交互过程。
HTTP协议的基本工作模式:一发一收,一问一答
这是HTTP协议的基本工作模式。客户端(通常是浏览器)发送一个请求到服务器,服务器接收到请求后,处理并返回一个响应给客户端。这种模式保证了请求和响应是一对一的对应关系,确保了数据的准确传输和处理的同步性。
HTTP协议的核心特点是基于请求-响应的通信模式,但在实际应用中,通过技术和协议的扩展(如HTTP/2、WebRTC等),可以实现更复杂的多发多收或多发一收的通信场景,以满足不同的网络应用需求。
-
多发一收:
这通常指的是上传大文件的情况。虽然HTTP协议本质上是一问一答的,但现代浏览器和服务器支持多部分上传(例如使用multipart/form-data
),允许客户端分批次发送数据块(比如文件的片段)到服务器,服务器则按顺序接收并重组这些数据块,从而实现大文件的上传。 -
一发多收:
这种情况通常与流媒体服务(如视频直播)相关。当用户搜索一个词条时,可能会有多个视频源或内容提供者。服务器可以返回多个资源的链接或数据,客户端可以同时连接这些资源进行数据抓取或流媒体播放,从而实现“一发多收”的效果。这通常通过HTTP/2或者其他高级网络技术来优化性能。 -
多发多收:
这类似于游戏串流服务(如Steam Link、NVIDIA GeForce Now)或远程桌面服务(如Moonlight)。这种情况下,客户端和服务器之间需要实时交换大量的数据流,例如,处理游戏画面的渲染、键盘和鼠标输入等。客户端和服务器都需要高强度地发送和接收数据,以保证流畅的游戏或远程桌面体验。这通常需要通过更复杂的协议(如WebRTC)或在HTTP/2的基础上进行优化来实现高效的“多发多收”。
二、认识URL
URL(Uniform Resource Locator,统一资源定位符)是用于定位互联网上资源的地址,标识互联网中唯一资源的一种标准化方式,通常我们称之为“网址”。URL 可以用来表示网页、图片、视频、文件等网络资源的位置。一个完整的 URL 包含多个部分,每部分都有特定的功能。下面我们详细解析一下 URL 的各个组成部分。
URL 的基本结构
一个典型的 URL 结构如下:
https://user:pass@www.example.com:443/path/to/resource?query=123#section
这个 URL 包含了多个部分,每个部分都有其特定的作用。下面将逐一解释。
1. 协议(Scheme)
协议(scheme)指定浏览器使用的协议来访问资源。常见的协议有:
- http:超文本传输协议(不安全版本)。
- https:安全的超文本传输协议(HTTP 的安全版本,使用加密)。
- ftp:文件传输协议。
- file:本地文件协议。
- mailto:电子邮件地址协议。
- data:用于嵌入数据(例如图片)的协议。
在上面的示例中,协议是 https,表示使用 HTTPS 协议访问资源。
2.登录信息(user:pass)
user:pass表示的是登录认证信息,包括登录用户的用户名和密码。虽然登录认证信息可以在URL中体现出来,但绝大多数URL的这个字段都是被省略的,因为登录信息可以通过其他方案交付给服务器。
3. 主机名(Hostname)
主机名(hostname)是资源所在的主机地址,通常是域名或 IP 地址。域名是更人性化的名称,方便记忆,而 IP 地址则是计算机用于识别的真实地址。
在上面的示例中,主机名是 www.example.com,它指向某个服务器,它表示的是服务器地址,也叫做域名,比如www.qq.com,www.baidu.com。
需要注意的是,我们用IP地址标识公网内的一台主机,但IP地址本身并不适合给用户看。比如说我们可以通过ping命令,分别获得www.baidu.com和www.qq.com这两个域名解析后的IP地址。
如果用户看到的是这两个IP地址,那么用户在访问这个网站之前并不知道这两个网站到底是干什么的,但如果用户看到的是www.baidu.com和www.qq.com这两个域名,那么用户至少知道这两个网站分别对应的是哪家公司,因此域名具有更好的自描述性。
实际我们可以认为域名和IP地址是等价的,在计算机当中使用的时候既可以使用域名,也可以使用IP地址。但URL呈现出来是可以让用户看到的,因此URL当中是以域名的形式表示服务器地址的。
【了解】域名
- 定义:域名是用于标识互联网上资源(如网站、邮件服务器等)的文本字符串。
- 作用:域名为用户提供了易于记忆和使用的地址,而不是复杂的IP地址。
- 结构:
- 顶级域名(TLD):如
.com
、.org
、.net
等。- 二级域名:如
example
在example.com
中。- 子域名:如
www
在www.example.com
中。IP地址
- 定义:IP地址是一个数字标签,用于标识网络设备的位置。
- 作用:IP地址用于在网络中唯一标识设备,并实现数据包的路由和传递。
- 分类:
- IPv4:使用32位地址,如
192.168.1.1
。- IPv6:使用128位地址,如
2001:0db8:85a3:0000:0000:8a2e:0370:7334
。域名和IP地址的关系
- 解析:域名系统(DNS,Domain Name System)负责将域名解析为对应的IP地址。这个过程称为DNS解析。
- 客户端请求:用户在浏览器中输入域名,如
www.example.com
。- DNS查询:浏览器向DNS服务器发送查询请求,寻求
www.example.com
对应的IP地址。- DNS响应:DNS服务器返回对应的IP地址,如
192.168.1.1
。- 建立连接:浏览器使用该IP地址与服务器建立连接,并请求资源。
DNS服务器
DNS服务器可以分为以下几类:
- 根域名服务器:负责顶级域名的解析。
- 顶级域名DNS服务器:负责特定顶级域名(如
.com
、.org
等)的解析。- 权威域名服务器:负责特定域名(如
example.com
)的解析。域名和IP地址的映射
- 静态映射:域名可以直接配置为指向特定IP地址,这种映射关系是固定的。
- 动态映射:某些情况下,域名可以通过动态DNS(DDNS)服务映射到临时或动态分配的IP地址。
示例
假设您在浏览器中输入
http://www.example.com
,以下是DNS解析过程的简要示例:
- 用户输入:
http://www.example.com
- DNS查询:
- 浏览器向本地DNS服务器发送查询请求。
- 本地DNS服务器向根域名服务器查询
.com
对应的顶级域名服务器。- 根域名服务器返回
.com
顶级域名服务器的地址。- 本地DNS服务器向
.com
顶级域名服务器查询example.com
对应的权威域名服务器。.com
顶级域名服务器返回example.com
权威域名服务器的地址。- 本地DNS服务器向
example.com
权威域名服务器查询www.example.com
对应的IP地址。example.com
权威域名服务器返回www.example.com
对应的IP地址,如192.168.1.1
。- IP地址获取:本地DNS服务器将IP地址返回给浏览器。
- 建立连接:浏览器使用获取的IP地址
192.168.1.1
与服务器建立连接,并请求资源。总结
域名和IP地址是互联网中不可或缺的两个元素,它们通过DNS系统紧密联系在一起。域名为用户提供了友好的标识符,而IP地址则是实际的网络地址。DNS系统充当了二者的桥梁,使得用户可以通过易记的域名访问互联网资源。
4. 端口号(Port)
端口号(port)用于指定服务器上的服务端口。不同的协议有默认的端口号,比如:
- HTTP 默认端口号是 80。
- HTTPS 默认端口号是 443。
在上面的示例中,端口号是 443,因为使用了 HTTPS 协议。如果使用默认端口号,端口号通常可以省略。
当我们使用某种协议时,该协议实际就是在为我们提供服务,现在这些常用的服务与端口号之间的对应关系都是明确的,所以我们在使用某种协议时实际是不需要指明该协议对应的端口号的,因此在URL当中,服务器的端口号一般也是被省略的。
5. 路径(Path)
路径(path)指定资源在服务器上的具体位置。路径通常类似于文件系统中的目录结构。
【IP地址标识了互联网中的唯一主机,端口号标识了该主机中服务器进程的唯一性,文件路径标识了该主机中访问资源的唯一性。】
访问服务器的目的是获取服务器上的某种资源,通过前面的域名和端口已经能够找到对应的服务器进程了,此时要做的就是指明该资源所在的路径。在上面的示例中,路径是 /path/to/resource,表示服务器上某个具体资源的位置。
比如我们打开浏览器输入百度的域名后,此时浏览器就帮我们获取到了百度的首页。
当我们发起网页请求时,本质是获得了这样的一张网页信息,然后浏览器对这张网页信息进行解释,最后就呈现出了对应的网页。
我们可以将这种资源称为网页资源,此外我们还会向服务器请求视频、音频、网页、图片等资源。HTTP之所以叫做超文本传输协议,而不叫做文本传输协议,就是因为有很多资源实际并不是普通的文本资源。
因此在URL当中就有这样一个字段,用于表示要访问的资源所在的路径。此外我们可以看到,这里的路径分隔符是/,而不是\,这也就证明了实际很多服务都是部署在Linux上的。
6. 查询参数(Query Parameters)
查询参数(query parameters)用于向服务器传递参数,通常以键值对的形式出现。查询参数以问号(?
)开始,多个参数之间用符号“&”分隔。
在上面的示例中,查询参数是 ?query=123,表示向服务器传递了一个名为 query
的参数,值为 123
。
如:我们在百度网页搜索HTTP, 此时可以看到URL中有很多参数,而在这众多的参数当中有一个参数wd(word),表示的就是我们搜索时的搜索关键字wd=HTTP。
因此客户端与服务端在进行网络通信时,是能够通过URL进行用户数据传送的。
7. 片段标识符(Fragment)
片段标识符(fragment)用于指定页面内的某个部分,通常是一个锚点(anchor)。片段标识符以井号(#
)开头,浏览器会滚动到页面中指定标识符的部分。
在上面的示例中,片段标识符是 #section,表示浏览器会滚动到页面中 id 为 section
的元素位置。
URLencode与URLdecode
像 / ? : 等这样的字符,已经被 url 当做特殊意义理解了。因此这些字符不能随意出现。比如,某个参数中需要带有这些特殊字符, 就必须先对特殊字符进行转义。
URLencode
和 URLdecode
是用于处理URL(Uniform Resource Locator,统一资源定位符)中特殊字符的编码和解码过程。这两个过程在网络通信和数据处理中非常重要,尤其是在涉及URL参数传输和表单数据提交时。
URLencode(URL编码)
URL编码,也称为百分号编码(Percent-Encoding),是一种编码机制,用于将URL中不允许的字符转换为允许的形式。URL只能包含一定范围的字符,如字母、数字和少数特殊字符(如-
、_
、.
和~
)。其他字符,如空格、中文、特殊符号等,在URL中是不允许的。
URL编码的过程如下:
- 将不允许的字符转换成一个“百分号”(
%
)后面跟着两个十六进制数字的形式。 - 例如:字符
空格
会被编码为%20
,字符&
会被编码为%26
,中文你好
可能会被编码为%E4%BD%A0%E5%A5%BD
。
URL编码的作用:
- 确保所有字符在URL中都能正确传输。
- 防止特殊字符被误解为URL的一部分(如&、?等字符在URL中具有特殊含义)。
// 将百分号编码的字符串转换回普通字符串
std::string URLdecode(const std::string& input) {std::string decoded;std::string::size_type i = 0;while (i < input.size()) {if (input[i] == '%') {// 将 %XX 转换为对应的字符if (i + 2 < input.size()) {std::string hex = input.substr(i + 1, 2);char c = static_cast<char>(std::stoi(hex, nullptr, 16));decoded += c;i += 3; // 跳过 %XX} else {// 无效的百分号编码decoded += input[i++];}} else if (input[i] == '+') {// 在某些情况下,'+' 被解释为空格decoded += ' ';++i;} else {decoded += input[i++];}}return decoded;
}
URLdecode(URL解码)
URL解码,也称为百分号解码(Percent-Decoding),是URL编码的逆过程。它用于将URL编码的字符还原为原始字符。
URL解码的过程如下:
- 将“百分号”(
%
)后面的两个十六进制数字转换回原始字符。 - 例如:
%20
会被解码为空格
,%26
会被解码为&
,%E4%BD%A0%E5%A5%BD
会被解码为中文你好
。
URL解码的作用:
- 当服务器接收到一个URL编码的数据时,需要进行解码以正确处理数据。
- 确保数据在传输过程中不会丢失或被误解。
示例
假设我们要发送一个URL参数,包括特殊字符和中文:
原始字符串:Hello World! & 你好
URL编码后:Hello%20World%21%20%26%20%E4%BD%A0%E5%A5%BD
在服务器接收到这个编码后的字符串后,需要进行URL解码,还原为:
解码后字符串:Hello World! & 你好
应用场景
- 表单数据提交:当用户通过网页表单提交数据时,表单中的特殊字符和中文会被URL编码后发送给服务器。
- URL参数传递:在URL中传递参数时,如果参数包含特殊字符或中文,需要进行URL编码。
- API请求:在调用API时,如果请求参数包含特殊字符,也需要进行URL编码。
URLencode和URLdecode在网络通信中确保数据在传输和处理过程中的一致性和正确性。
// 作用:将输入的字符串按照 URL 编码规则进行编码,并返回编码后的字符串
std::string URLencode(const std::string& input) {// std::ostringstream 是 C++ 标准库中的一个类,继承自 std::ostream,用于进行字符串的流式输出。// 它的功能类似于 std::cout,但输出内容会被写入到一个字符串中。std::ostringstream escaped;// escaped.fill('0'); 设置流输出时填充字符为 '0',主要用于后续的十六进制输出,确保不足两位时用 0 填充。escaped.fill('0');// escaped << std::hex; 设置流的输出基数为十六进制,这样后续的整数输出将以十六进制格式进行。escaped << std::hex;for (std::string::const_iterator i = input.begin(); i != input.end(); ++i) {char c = *i;// 保留未保留字符:字母和数字if (isalnum(c) || c == '-' || c == '_' || c == '.' || c == '~') {escaped << c;} else {// 将特殊字符转换为 %XX 形式escaped << std::uppercase;escaped << '%' << std::setw(2) << (int)(unsigned char)c;escaped << std::nouppercase;// 如果字符 c 不是字母、数字或者那些不需要编码的字符,那么它需要被编码。// std::uppercase 用于确保接下来的十六进制字母输出为大写形式。// escaped << '%' 输出一个百分号 %,这是 URL 编码中表示特殊字符的格式。// (int)(unsigned char)c 将字符 c 转换为其对应的 ASCII 整数值(因为 char 可能是有符号的,所以先转换为 unsigned char 确保得到正确的无符号值)。// std::setw(2) 设置输出宽度为 2,这样十六进制数不足两位时会用之前设置的填充字符 '0' 来补齐。// escaped << std::nouppercase; 恢复十六进制字母输出为小写(默认情况下输出小写字母)。}}return escaped.str(); // escaped.str() 从 std::ostringstream 对象中提取最终生成的字符串,并将其返回
}
三、认识HTTP协议
应用层常见的协议有HTTP和HTTPS,传输层常见的协议有TCP,网络层常见的协议是IP,数据链路层对应就是MAC帧了。其中下三层是由操作系统或者驱动帮我们完成的,它们主要负责的是通信细节。如果应用层不考虑下三层,在应用层自己的心目当中,它就可以认为自己是在和对方的应用层在直接进行数据交互。
下三层负责的是通信细节,而应用层负责的是如何使用传输过来的数据,两台主机在进行通信的时候,应用层的数据能够成功交给对端应用层,因为网络协议栈的下三层已经负责完成了这样的通信细节,而如何使用传输过来的数据就需要我们去定制协议,这里最典型的就是HTTP协议。
HTTP是基于请求和响应的应用层服务,作为客户端,你可以向服务器发起request,服务器收到这个request后,会对这个request做数据分析,得出你想要访问什么资源,然后服务器再构建response,完成这一次HTTP的请求。这种基于request&response这样的工作方式,我们称之为cs或bs模式,其中c表示client,s表示server,b表示browser。
由于HTTP是基于请求和响应的应用层访问,因此我们必须要知道HTTP对应的请求格式和响应格式,这就是学习HTTP的重点。
四、HTTP请求格式
我们将浏览器作为客户端访问自己编写的服务端,服务端将接收到的HTTP请求打印出来,我们来见一见一个完整的HTTP请求是什么样子的。【注意,要确保服务端的端口号设置为开放状态,不然浏览器无法访问】。
在网络协议栈中,应用层的下一层叫做传输层,而HTTP协议底层通常使用的传输层协议是TCP协议,因此我们可以用套接字编写一个TCP服务器,然后启动浏览器访问我们的这个服务器。
我们来编写一个简单的测试代码,作为接受访问的服务端:
#include <sys/types.h>
#include <sys/socket.h>
#include <string>
#include <arpa/inet.h>
#include <iostream>
#include <unistd.h>
#include <sys/wait.h>static const uint16_t DEFAULT_PORT = 8888; // 确保自己云服务器上的该端口号已经被打开
static const int BUFFER_SIZE = 1024;int main(int argc, char *argv[])
{// 1、创建监听套接字int listen_sockfd = socket(AF_INET, SOCK_STREAM, 0);if(listen_sockfd < 0){std::cout << "Socket false!" << std::endl;exit(-1);}// 2、绑定本地ip和端口号 struct sockaddr_in local_addr;local_addr.sin_addr.s_addr = INADDR_ANY; // 接收发送至该主机上的所有请求local_addr.sin_port = htons(DEFAULT_PORT); // 将端口号转换为网络字节序local_addr.sin_family = AF_INET; // 地址族设置为IPV4if(bind(listen_sockfd, (struct sockaddr *)&local_addr, sizeof(local_addr)) < 0){std::cout << "Bind false!" << std::endl;exit(-1);}// 3、将套接字设置为监听状态if(listen(listen_sockfd, 6) < 0){std::cout << "Listen false!" << std::endl;exit(-1);}while (true){// 3、接收来自客户端的连接请求,获取i/o套接字struct sockaddr_in from_client;socklen_t len = sizeof(from_client);int io_sockfd = accept(listen_sockfd, (struct sockaddr *)&from_client, &len);if(io_sockfd < 0){continue;}pid_t pid = fork(); // 爷爷进程创建父亲进程if(pid == 0) // 父亲进程{close(listen_sockfd); // 关闭无用描述符pid_t c_pid = fork(); // 创建孙子进程去执行io操作,子进程直接退出if(c_pid == 0) // 子进程直接退出,孙子进程变为孤儿进程由系统领养{// 4、接收来自客户端的请求信息char info_buffer[BUFFER_SIZE];ssize_t r_num = recv(io_sockfd, info_buffer, BUFFER_SIZE - 1, 0);info_buffer[r_num] = '\0';// 5、将接收到的信息打印出来std::cout << info_buffer << std::endl;close(io_sockfd); // 执行完io操作直接关闭描述符}else if(c_pid > 0){exit(1); // 父亲进程直接退出}}else if(pid > 0){close(io_sockfd); // 关闭无用描述符int res = waitpid(pid, nullptr, 0); // 爷爷进程等待回收父亲进程资源if(res < 0){std::cout << std::endl << "Waitpid false!" << std::endl;exit(-1);}}}return 0;
}
需要注意的是,上述代码是有BUG的!它并不能确保我们每次接收到的请求都是一个完整的HTTP请求,我们并没有对其做请求完整性的检测!!!此代码仅做测试使用。
在网页浏览器输入我们的IP地址和端口号:
可以看到的是,我们所访问的页面并没有传递任何信息。这是因为服务端并未向客户端返回访问结果数据。我们在此处暂不做处理。
我们分别使用了电脑浏览器和手机浏览器访问了我们的服务端,并将收到的HTTP请求打印出来,结果如下:
【说明】: 浏览器向我们的服务器发起HTTP请求后,因为我们的服务器没有对进行响应,此时浏览器就会认为服务器没有收到,然后再不断发起新的HTTP请求,因此虽然我们只用浏览器访问了一次,但会受到多次HTTP请求。
可见,HTTP的请求是有固定格式的!那么具体格式是怎样的呢?如下图所示:
在HTTP请求中,行分隔符是 \r\n
(即回车符和换行符,CRLF)。HTTP协议使用CRLF来分隔不同的行和字段。
以下是一个典型的HTTP请求示例:
GET /index.html HTTP/1.1\r\n
Host: www.example.com\r\n
User-Agent: Mozilla/5.0\r\n
Accept: text/html\r\n
\r\n
在这个示例中:
- 第一行包含请求方法(GET)、请求路径(/index.html)和HTTP版本(HTTP/1.1),并且以
\r\n
结尾。 - 接下来的每一行包含一个HTTP头部字段(如Host、User-Agent、Accept),每个字段也以
\r\n
结尾。 - 最后一个头部字段之后是一个空行,由两个
\r\n
组成,表示头部字段的结束和请求体的开始(如果有的话)。
1. 请求行(Request Line)
-
格式:
[请求方法] [URL] [HTTP版本]
-
请求方法:常见的请求方法包括:
GET
:请求指定的资源,通常用于获取数据。POST
:向指定资源提交数据,常用于表单提交。PUT
:更新指定资源。DELETE
:删除指定资源。
-
URL:请求的目标地址,这可以是绝对路径或相对路径,包括查询参数。
需要注意到的是:
1、当我们未指明访问路径时,浏览器默认访问的是根目录。但url当中的 “ / ” 不能称之为我们云服务器上根目录,这个/表示的是web根目录,这个web根目录可以是你的机器上的任何一个目录,这个是可以自己指定的,不一定就是Linux的根目录。
2、由于浏览器发起请求时默认用的就是HTTP协议,因此我们在浏览器的url框当中输入网址时可以不用指明HTTP协议。
其中请求行当中的url一般是不携带域名以及端口号的,因为在请求报头中的Host字段当中会进行指明,请求行当中的url表示你要访问这个服务器上的哪一路径下的资源。如果浏览器在访问我们的服务器时指明要访问的资源路径,那么此时浏览器发起的HTTP请求当中的url也会跟着变成该路径,如下:
-
HTTP版本:表示使用的HTTP协议版本,如HTTP/1.1或HTTP/2.
-
示例:
GET /index.html HTTP/1.1
2. 请求报头(Request Headers)
-
格式:
key: value
,每个报头占一行,是键值对结构,键和值之间用 冒号+空格 分割。 -
用途:请求报头包含了关于请求的额外信息,例如:
- Host:请求的主机名。例如:
Host: www.example.com
- User-Agent:客户端的信息,用于标识发起请求的应用程序或浏览器。例如:
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64) AppleWebKit/537.36 (KHTML, like Gecko) Chrome/86.0.4240.75 Safari/537.36
- Content-Type:请求正文的数据类型,如果有请求正文。常见的比如
application/json
、application/x-www-form-urlencoded
等。 - Content-Length:请求正文的长度。
- Host:请求的主机名。例如:
-
示例:
Host: www.example.com User-Agent: Mozilla/5.0 Content-Type: application/x-www-form-urlencoded
3. 空行(Blank Line)
-
作用:空行实际就是字符串"\r\n" ,用来分隔请求报头和请求正文。
-
示例:
-
"\r\n"
-
4. 请求正文(Request Body)
-
内容:请求正文是包含用户相关信息或数据的部分,通常在使用
POST
或PUT
方法时才会出现。请求正文可以为空字符串。 -
Content-Length:如果请求正文存在,请求报头中通常会包含一个
Content-Length
属性以指示请求正文的字节长度。 -
示例:
在一个POST
请求中,可能会包含用户提交的表单数据,例如:name=John&age=30
示例完整HTTP请求
以下是一个完整的HTTP POST请求示例:
POST /submit HTTP/1.1 // 请求行
Host: www.example.com // 请求报头
User-Agent: Mozilla/5.0 // 请求报头
Content-Type: application/x-www-form-urlencoded // 请求报头
Content-Length: 27 // 请求报头// 空行
name=John&age=30 // 请求正文
- 请求行:包含请求方法、URL和HTTP版本。
- 请求报头:包含附加的信息,以key: value形式展示。
- 空行:用于分隔请求报头和请求正文。
- 请求正文:包含用户提交的数据,长度由
Content-Length
属性标识。
其中,前面三部分是一般是HTTP协议自带的,是由HTTP协议自行设置的,而请求正文一般是用户的相关信息或数据,如果用户在请求时没有信息要上传给服务器,此时请求正文就为空字符串。
五、HTTP请求中常见的请求方法
方法 | 用途 | 示例 | 特性 | 支持的HTTP版本 |
---|---|---|---|---|
GET | 获取资源 | GET /index.html HTTP/1.1 | 幂等、可缓存、数据通过URL传递 | HTTP/1.0, HTTP/1.1, HTTP/2, HTTP/3 |
POST | 提交数据 | POST /submit-form HTTP/1.1 | 非幂等、数据通过请求体传递、无缓存 | HTTP/1.0, HTTP/1.1, HTTP/2, HTTP/3 |
PUT | 更新或替换资源 | PUT /user/123 HTTP/1.1 | 幂等、完全替换 | HTTP/1.1, HTTP/2, HTTP/3 |
DELETE | 删除资源 | DELETE /user/123 HTTP/1.1 | 幂等、结果不可逆 | HTTP/1.1, HTTP/2, HTTP/3 |
HEAD | 获取资源的元数据 | HEAD /index.html HTTP/1.1 | 无响应体、快速检查 | HTTP/1.0, HTTP/1.1, HTTP/2, HTTP/3 |
OPTIONS | 获取支持的HTTP方法 | OPTIONS /resource HTTP/1.1 | 获取支持方法、常用于CORS预检 | HTTP/1.1, HTTP/2, HTTP/3 |
PATCH | 部分更新资源 | PATCH /user/123 HTTP/1.1 | 部分更新、非幂等 | HTTP/1.1, HTTP/2, HTTP/3 |
LINK | 建立和资源之间的联系 | LINK /resource HTTP/1.0 | 表示资源之间的关系,通常用于描述关系状态 | HTTP/1.0 |
UNLINK | 断开连接关系 | UNLINK /resource HTTP/1.0 | 表示解除资源之间的链接 | HTTP/1.0 |
CONNECT | 要求用隧道协议连接代理 | CONNECT server.example.com:80 HTTP/1.1 | 用于将请求按隧道协议连接到服务器,主要用于HTTP代理 | HTTP/1.1, HTTP/2, HTTP/3 |
TRACE | 追踪路径 | TRACE /resource HTTP/1.1 | 用于诊断目的,显示请求经过的路径 | HTTP/1.1 |
【名词解释:幂等】:对于一个操作,如果执行一次或多次操作,结果是一样的,那么该操作就是“幂等”的。如:
-
GET:获取资源。无论你发送多少次GET请求,服务器的状态不会改变,因为GET请求只是读取数据,不会修改资源。所以GET方法是“幂等”的。
-
POST:提交数据。每次POST请求都会创建一个新的资源或执行一个新的操作,因此重复发送POST请求可能会导致多次创建资源或多次执行操作。因此POST方法是“非幂等”的。
六、GET和POST的作用与区别
GET和POST是HTTP协议中最常见的两种请求方法,它们都用于向服务器发送请求,但用途和行为有所不同:
GET请求:
-
作用:
- 获取服务器上的特定资源。
- 通常用于请求服务器发送某个资源。
- 可以被缓存、收藏为书签、保留在浏览器历史记录中。
-
特点:
- 请求的数据会附加在URL之后,以键值对的形式出现,通过
?
连接。键值对之间通过&分隔开来。 - 数据大小有限制,因为URL的长度有限制。
- 不安全,因为数据明文出现在URL中,可以被浏览器历史、服务器日志等记录。
- 只用于获取数据,不应当用来修改服务器上的数据。
- 请求的数据会附加在URL之后,以键值对的形式出现,通过
-
使用场景:
- 搜索操作。
- 打开网页链接。
- 请求静态资源。
POST请求:
-
作用:
- 向服务器提交数据,通常用于导致服务器状态改变的操作。
- 可以发送大量数据。
-
特点:
- 请求的数据在请求体中,不在URL中。
- 数据大小限制较少,取决于服务器配置和网络条件。
- 相对安全,因为数据不会出现在URL中。
- 可以用于发送敏感信息,如密码等。
-
使用场景:
- 提交表单数据。
- 上传文件。
- 发起可能导致服务器状态改变的请求,如创建用户账户、提交订单等。
区别:
-
数据可见性:
GET请求的数据在URL中可见,POST请求的数据在请求体的请求正文中。 -
数据大小限制:
GET受到 URL 长度限制,不同的浏览器和服务器对 URL 长度的限制有所不同,但一般来说长度有限。通常不适合传递大量数据。POST理论上对数据长度没有严格限制,主要取决于服务器的设置。 -
安全性:
GET请求不安全,因为数据在URL中;POST请求相对安全。但两者的数据都不是绝对安全的。准确的来说,POST方法由于将数据放在了请求正文中,虽然无法像URL中的数据可以直观看到,但是仍然可以通过一些手段得到正文中的数据,只能说POST相较于GET具有较好的数据私密性。如果想让数据变得“安全”,HTTPS协议可以更好的为数据提供加密服务。 -
幂等性:
- GET请求是幂等的,即多次执行相同的GET请求,结果相同,不会改变服务器状态。
- POST请求不是幂等的,多次执行可能会多次修改服务器状态。
-
可缓存性:
- GET 可以被缓存,因为其请求结果通常可以被重复使用,以提高性能。如果请求的资源没有发生变化,浏览器可以直接使用缓存的结果,减少服务器负载。
- POST请求一般不被缓存,因为 POST 请求通常会对服务器状态产生影响,每次请求的结果可能不同。
-
书签和历史记录:
GET请求可以被保存为书签和历史记录,POST请求不可以。 -
请求/响应模式:
- GET请求通常用于请求数据,响应模式是请求-响应。
- POST请求用于提交数据,响应模式可能是请求-响应,也可能是只响应。
总的来说,GET和POST的主要区别在于它们的用途和行为:GET用于获取数据,POST用于提交数据。
我们借用Psotman软件来见一见GET方法和POST方法的HTTP请求的区别:
要真正了解GET,还需要使用form表单,同时还需要构建我们自己的基于TCP网络通信的使用HTTP协议格式的服务端。在如下博客中我们再做更细致的讲解:
【Linux】网络编程:实现一个简易的基于HTTP协议格式、TCP传输的服务器,处理HTTP请求并返回HTTP响应;GET方法再理解-CSDN博客
七、HTTP响应格式
在Linux操作系统中,我们可以使用telnet命令连接到一个远程主机的特定端口。
telnet [主机名或IP地址] [端口号]
例如,我们可以进行如下操作来连接百度的服务器并访问它的首页,来见一见HTTP应答的格式:
得到应答如下:
1. 状态行(Status Line)
- 格式:
[HTTP版本] [状态码] [状态码描述]
- 示例:
HTTP/1.1 200 OK
- HTTP版本:表示使用的HTTP协议版本,如HTTP/1.1。
- 状态码:表示服务器处理请求的结果,如200表示成功,404表示未找到资源,500表示服务器内部错误等。
- 状态码描述:是对状态码的简短描述,如“OK”、“Not Found”等。
2. 响应报头(Response Headers)
- 格式:
key: value
,每个报头占一行,是键值对结构,键和值之间用 冒号+空格 分割。 - 示例:
Content-Type: text/html; charset=UTF-8 Content-Length: 1234 Server: Apache/2.4.1 (Unix)
- 内容:响应报头包含各种属性,用于描述响应的附加信息,如内容类型、内容长度、服务器类型等。这些属性可以帮助客户端理解如何处理响应正文。
3. 空行(Blank Line)
- 作用:空行实际就是字符串"\r\n" ,用来分隔响应报头和响应正文。
- 示例:
"\r\n"
4. 响应正文(Response Body)
- 格式:可以是任何类型的数据,如HTML页面、JSON数据、图像等。例如:如果返回的是HTML页面,那么页面的内容就在响应正文中。
- 示例:
<html> <head><title>Example</title></head> <body> <h1>Hello, World!</h1> </body> </html>
- 内容:响应正文包含服务器返回的实际数据。如果响应正文为空,则这一部分可以省略。
- Content-Length:如果响应正文存在,通常响应报头中会有一个
Content-Length
属性来标识响应正文的长度,以字节为单位。
示例完整HTTP响应
HTTP/1.1 200 OK // 状态行
Content-Type: text/html; charset=UTF-8 // 响应报头
Content-Length: 68 // 响应报头
Server: Apache/2.4.1 (Unix) // 响应报头// 空行
<html> // 响应正文
<head><title>Example</title></head>
<body>
<h1>Hello, World!</h1>
</body>
</html>
- 状态行:包含HTTP版本、状态码和状态码描述。
- 响应报头:包含响应的属性,以
key: value
形式展示。 - 空行:分隔响应报头和响应正文。
- 响应正文:包含服务器返回的实际数据,长度由
Content-Length
标识。
状态码详解
HTTP 响应状态码是服务器对客户端请求的响应结果的一种标准化表示。它们帮助客户端理解服务器处理请求的状态,并采取相应的措施。状态码分为五类,每类以不同的数字开头,分别表示不同类型的响应。以下是详细的解释:
状态码分类 | 状态码范围 | 描述 |
---|---|---|
1xx(信息性状态码) | 100 - 199 | 表示请求已被接收,继续处理。 |
2xx(成功状态码) | 200 - 299 | 用于表示请求成功的情况,客户端可以继续操作或处理返回的数据。 |
3xx(重定向状态码) | 300 - 399 | 用于表示需要客户端进行重定向的情况,客户端应根据新的URL继续请求。 |
4xx(客户端错误状态码) | 400 - 499 | 用于表示客户端请求错误的情况,客户端应检查请求并进行修正。 |
5xx(服务器错误状态码) | 500 - 599 | 用于表示服务器内部错误的情况,客户端通常需要等待或重试请求。 |
常见的状态码及解释
分类 | 状态码 | 状态码名称 | 解释 |
---|---|---|---|
1xx(信息性状态码) | 100 | Continue | 服务器已接收到请求头,并且客户端应继续发送请求体。 |
1xx(信息性状态码) | 101 | Switching Protocols | 服务器根据客户端的请求切换协议。通常用于WebSocket连接。 |
2xx(成功状态码) | 200 | OK | 请求成功,服务器已成功处理请求并返回相应的内容。 |
2xx(成功状态码) | 201 | Created | 请求成功,并且在服务器上创建了一个新的资源。通常用于POST请求。 |
2xx(成功状态码) | 202 | Accepted | 请求已被接受,但处理尚未完成。通常用于异步处理。 |
2xx(成功状态码) | 204 | No Content | 请求成功,但没有返回任何内容。通常用于删除资源或更新资源的请求。 |
3xx(重定向状态码) | 300 | Multiple Choices | 请求的资源有多个选择,客户端可以选择其中一个。 |
3xx(重定向状态码) | 301 | Moved Permanently | 请求的资源已永久移动到新位置,客户端应更新请求的URL。 |
3xx(重定向状态码) | 302 | Found(或Moved Temporarily) | 请求的资源临时移动到新位置,客户端应继续使用原URL。 |
3xx(重定向状态码) | 304 | Not Modified | 资源未修改,客户端可以使用缓存的版本。通常用于条件请求。 |
3xx(重定向状态码) | 307 | Temporary Redirect | 请求的资源临时移动到新位置,客户端应继续使用原URL。与302类似,但明确要求客户端保持请求方法不变。 |
3xx(重定向状态码) | 308 | Permanent Redirect | 请求的资源已永久移动到新位置,客户端应更新请求的URL。与301类似,但明确要求客户端保持请求方法不变。 |
4xx(客户端错误状态码) | 400 | Bad Request | 请求格式错误,服务器无法理解请求。 |
4xx(客户端错误状态码) | 401 | Unauthorized | 请求需要身份验证,客户端未提供有效的身份验证信息。 |
4xx(客户端错误状态码) | 403 | Forbidden | 服务器理解请求,但拒绝执行请求。通常是因为客户端没有权限访问资源。 |
4xx(客户端错误状态码) | 404 | Not Found | 请求的资源在服务器上不存在。 |
4xx(客户端错误状态码) | 405 | Method Not Allowed | 请求方法不被允许,例如使用POST方法请求只支持GET方法的资源。 |
4xx(客户端错误状态码) | 409 | Conflict | 请求与服务器当前状态冲突,通常用于资源冲突,如重复的资源创建请求。 |
4xx(客户端错误状态码) | 410 | Gone | 请求的资源在服务器上已不存在,且没有新的URL指向该资源。与404类似,但410表示资源曾经存在。 |
4xx(客户端错误状态码) | 429 | Too Many Requests | 客户端发送了过多的请求,超过了服务器的限制。 |
5xx(服务器错误状态码) | 500 | Internal Server Error | 服务器在处理请求时发生了未知的错误。 |
5xx(服务器错误状态码) | 501 | Not Implemented | 服务器不支持请求的功能,无法完成请求。 |
5xx(服务器错误状态码) | 502 | Bad Gateway | 服务器作为网关或代理,从上游服务器接收到无效响应。 |
5xx(服务器错误状态码) | 503 | Service Unavailable | 服务器暂时无法处理请求,通常是因为服务器过载或维护。 |
5xx(服务器错误状态码) | 504 | Gateway Timeout | 服务器作为网关或代理,未能在规定时间内从上游服务器接收到响应。 |
5xx(服务器错误状态码) | 505 | HTTP Version Not Supported | 服务器不支持请求中使用的HTTP协议版本。 |
Redirection(重定向状态码)
重定向就是通过各种方法将各种网络请求重新定个方向转到其它位置,此时这个服务器相当于提供了一个引路的服务。
重定向又可分为临时重定向和永久重定向,其中状态码301表示的就是永久重定向,而状态码302和307表示的是临时重定向。
临时重定向和永久重定向本质是影响客户端的标签,决定客户端是否需要更新目标地址。如果某个网站是永久重定向,那么第一次访问该网站时由浏览器帮你进行重定向,但后续再访问该网站时就不需要浏览器再进行重定向了,此时你访问的直接就是重定向后的网站。而如果某个网站是临时重定向,那么每次访问该网站时如果需要进行重定向,都需要浏览器来帮我们完成重定向跳转到目标网站。
临时重定向演示
进行临时重定向时需要用到Location字段,Location字段是HTTP报头当中的一个属性信息,该字段表明了你所要重定向到的目标网站。
我们这里要演示临时重定向,可以将HTTP响应当中的状态码改为307,然后跟上对应的状态码描述,此外,还需要在HTTP响应报头当中添加Location字段,这个Location后面跟的就是你需要重定向到的网页,比如我们这里将其设置为CSDN的首页。
#include <iostream>
#include <fstream>
#include <string>
#include <cstring>
#include <unistd.h>
#include <sys/wait.h>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
using namespace std;int main()
{//创建套接字int listen_sock = socket(AF_INET, SOCK_STREAM, 0);if (listen_sock < 0){cerr << "socket error!" << endl;return 1;}//绑定struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(8888);local.sin_addr.s_addr = htonl(INADDR_ANY);if (bind(listen_sock, (struct sockaddr*)&local, sizeof(local)) < 0){cerr << "bind error!" << endl;return 2;}//监听if (listen(listen_sock, 5) < 0){cerr << "listen error!" << endl;return 3;}//启动服务器struct sockaddr peer;memset(&peer, 0, sizeof(peer));socklen_t len = sizeof(peer);for (;;){int sock = accept(listen_sock, (struct sockaddr*)&peer, &len);if (sock < 0){cerr << "accept error!" << endl;continue;}if (fork() == 0){ //父亲进程close(listen_sock);if (fork() > 0){ //父亲进程exit(0);}//孙子进程char buffer[1024];recv(sock, buffer, sizeof(buffer), 0); //读取HTTP请求cout << "--------------------------http request begin--------------------------" << endl;cout << buffer << endl;cout << "---------------------------http request end---------------------------" << endl;//构建HTTP响应string status_line = "http/1.1 307 Temporary Redirect\n"; //状态行string response_header = "Location: https://www.csdn.net/\n"; //响应报头string blank = "\n"; //空行string response = status_line + response_header + blank; //响应报文//响应HTTP请求send(sock, response.c_str(), response.size(), 0);close(sock);exit(0);}//爷爷进程close(sock);waitpid(-1, nullptr, 0); //等待父亲进程}return 0;
}
我们在网址导航栏输入我们服务器的IP地址和端口号:
当点击回车键后,就会自动转到CSDN的首页网页:
八、HTTP中的常见的请求报头属性
常见的HTTP的报头属性【Key】:
属性名 | 说明 |
---|---|
Accept | 告知服务器客户端可以接受的MIME类型(如text/html, application/json)。 |
Accept-Charset | 客户端可以接受的字符集(如UTF-8)。 |
Accept-Encoding | 客户端支持的内容编码(如gzip, deflate)。 |
Accept-Language | 客户端优先选择的语言(如en-US, zh-CN)。 |
User-Agent | 客户端的软件信息(如浏览器类型和版本)。 |
Cache-Control | 指定缓存机制如何处理请求和响应(如no-cache, max-age=3600)。 |
Connection | 控制当前连接的选项(如keep-alive)。 |
Content-Length | 请求体的长度(字节数)。 |
Content-Type | 请求体的MIME类型(如application/json)。 |
Cookie | 客户端发送给服务器的cookie信息。 |
Host | 请求的目标主机和端口号(如example.com:8080)。 |
Origin | 指示请求的发起源,常用于CORS(跨域资源共享)。 |
Referer | 指示请求的来源URL,即当前页面是从哪个页面跳转过来的。 |
Location | 搭配3XX状态码使用,告诉客户端接下来要去哪里访问。 |
Host
Host字段表明了客户端要访问的服务的IP和端口,比如当浏览器访问我们的服务器时,浏览器发来的HTTP请求当中的Host字段填的就是我们的IP和端口。但客户端不就是要访问服务器吗?为什么客户端还要告诉服务器它要访问的服务对应的IP和端口?
因为有些服务器实际提供的是一种代理服务,也就是代替客户端向其他服务器发起请求,然后将请求得到的结果再返回给客户端。在这种情况下客户端就必须告诉代理服务器它要访问的服务对应的IP和端口,此时Host提供的信息就有效了。
User-Agent
User-Agent代表的是客户端对应的操作系统和浏览器的版本信息。
比如当我们用电脑下载某些软件时,它会自动向我们展示与我们操作系统相匹配的版本,这实际就是因为我们在向目标网站发起请求的时候,User-Agent字段当中包含了我们的主机信息,此时该网站就会向你推送相匹配的软件版本。
Referer
Referer代表的是你当前是从哪一个页面跳转过来的。Referer记录上一个页面的好处一方面是方便回退,另一方面可以知道我们当前页面与上一个页面之间的相关性。同时,假如有以下需求:禁止从页面B跳转至页面A,我们就可以在后台服务器分析HTTP中的请求,查看Referer属性的键值是否是页面B。如果是,我们在后台设置禁止跳转的处理策略即可。
下面我们来演示一下:
我们首先启动我们的服务端
接着在网址导航栏中输入我们要访问的IP地址和端口号
我们就会进入到我们所设置的首页页面
此时我们来观察一下浏览器发送给服务端的HTTP请求,可以看到其中并不包含Referer属性:
但是当我们点击该页面中的“链接”后,网页会跳转到另一个页面,同时也会向服务端发送一个新的HTTP请求:
在上述请求当中,我们观察到请求报文中包含了Referer属性和页面跳转前的页面地址。
Connection:主要用于控制和管理客户端与服务器之间的连接状态
核心作用
• 管理持久连接: 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 连接。
HTTP/1.0是通过request&response的方式来进行请求和响应的,HTTP/1.0常见的工作方式就是客户端和服务器先建立连接,然后客户端发起请求给服务器,服务器再对该请求进行响应,然后立马断开TCP连接。
但如果一个连接建立后客户端和服务器只进行一次交互,就将连接关闭,就太浪费资源了,因此现在主流的HTTP/1.1是支持长连接的。所谓的长连接就是建立连接后,客户端可以不断的向服务器写入多个HTTP请求,而服务器在上层依次读取这些请求,而不是处理完一个请求后就关闭连接。如果HTTP请求或响应报头当中的Connect字段对应的值是Keep-Alive,就代表支持长连接。
九、Cookie与Session
在Web开发和应用中,Cookie 和 Session 是两种常见的机制,用于在客户端和服务器之间维护状态。尽管它们都用于跟踪用户会话,但它们的工作方式和用途有所不同。下面详细解释一下 Cookie 和 Session:
1. Cookie
1.1 基本概念
- 定义:Cookie 是存储在客户端(浏览器)中的一个小型文本文件,用于存储用户的会话信息。
- 用途:主要用于在客户端和服务器之间传递信息,例如用户身份验证状态、用户偏好设置等。
1.2 工作原理
- 创建:当客户端(例如浏览器)第一次访问服务器时,服务器可以通过HTTP响应头的
Set-Cookie
字段发送一个或多个Cookie到客户端。 - 存储:客户端接收到Cookie后,会将其存储在本地(通常是在浏览器的Cookie数据库中)。
- 发送:客户端在后续请求中会自动将Cookie发送到服务器,通过HTTP请求头的
Cookie
字段。 - 读取:服务器接收到请求后,可以读取Cookie中的信息,并根据这些信息做出相应的响应。
1.3 特点
- 存储在客户端:Cookie 存储在用户的浏览器中,这使得它们容易受到攻击,如XSS(跨站脚本攻击)和CSRF(跨站请求伪造)。
- 容量限制:每个Cookie的大小通常限制在4KB左右,每个域名下最多可以存储约20-50个Cookie。
- 有效期:Cookie可以设置一个过期时间(
Expires
或Max-Age
属性),过期后自动删除。如果不设置过期时间,Cookie会成为会话Cookie,关闭浏览器后自动删除。
2. Session
2.1 基本概念
- 定义:Session 是服务器端用于存储用户会话信息的机制。
- 用途:用于跟踪用户会话,存储用户身份验证状态、购物车内容等敏感或大型数据。
2.2 工作原理
- 创建:当用户第一次访问服务器时,服务器会创建一个唯一的Session ID,并将其存储在服务器端(通常是在内存或数据库中)。
- 回传:服务器将Session ID通过HTTP响应头的
Set-Cookie
字段发送给客户端,通常作为一个Cookie。 - 传递:客户端(例如浏览器)在后续请求中会自动将Session ID通过HTTP请求头的
Cookie
字段发送回服务器。 - 读取:服务器接收到请求后,根据Session ID找到对应的Session信息,并根据这些信息做出相应的响应。
2.3 特点
- 存储在服务器端:Session 信息存储在服务器端,因此相对更安全,不易受到客户端攻击。
- 容量限制相对较少:Session 通常没有容量限制,但服务器端的存储容量可能会限制Session的大小。
- 依赖Cookie:尽管Session信息存储在服务器端,但Session ID通常通过Cookie传递。如果客户端禁用了Cookie,可以通过URL重写等方式传递Session ID。
3. 对比与总结
特性 | Cookie | Session |
---|---|---|
存储位置 | 客户端 | 服务器端 |
安全性 | 相对较低,易受客户端攻击 | 相对较高,不易受客户端攻击 |
容量限制 | 每个Cookie 4KB左右,每个域名20-50个Cookie | 通常没有容量限制,取决于服务器存储容量 |
依赖机制 | 独立存在,用于存储少量数据 | 依赖Cookie或URL重写传递Session ID |
使用场景 | 存储用户偏好、跟踪会话状态等 | 存储用户身份验证状态、购物车内容等敏感信息 |
4. 适用场景
- Cookie:适用于存储少量、非敏感信息,如用户偏好设置、跟踪会话状态等。
- Session:适用于存储大量、敏感信息,如用户身份验证状态、购物车内容等。
5. 安全性和最佳实践
-
Cookie:
- 设置
HttpOnly
属性,防止JavaScript访问Cookie,减少XSS攻击风险。 - 设置
Secure
属性,确保Cookie只在HTTPS连接中传输,减少中间人攻击风险。 - 使用
SameSite
属性,减少CSRF攻击风险。 - 设置合适的过期时间,避免长期存储敏感信息。
- 设置
-
Session:
- 使用加密的Session ID,防止Session劫持。
- 定期更新Session ID,增加安全性。
- 在服务器端设置合适的Session过期时间。
实现一个简单的HTTP服务器:【Linux】网络编程:实现一个简易的基于HTTP协议格式、TCP传输的服务器,处理HTTP请求并返回HTTP响应;GET方法再理解-CSDN博客