应用层协议HTTP
一、HTTP协议
(1)虽然我们说,应用层协议是我们程序员自己定的。但实际上,已经有大佬们定义了一些现成的,又非常好用的应用层协议,供我们直接参考使用,HTTP(超文本传输协议)就是其中之一。
(2)在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的议。
它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如HTML文档)。
(3)HTTP协议是客户端与服务器之间通信的基础。客户端通过HTTP协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。
二、认识URL
URL(UniformResourceLocator,统一资源定位符),俗称“网址”,是互联网上用来唯一定位资源
(网页、图片、视频、接口等)的标准化字符串。


三、urlencode和urldecode
像口/?:口等这样的字符,已经被url当做特殊意义理解了,因此这些字符不能随意出现,比如,某
个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。
转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。
注意:这个编码过程是网页自己做的,服务器拿到的是已经转换过的
四、HTTP协议请求与响应格式
4.1HTTP请求格式
(1)首行:[方法]+[url]+[版本]。
(2)Header:请求的属性,冒号分割的键值对;每组属性之间使用Vr\n分隔;遇到空行表示Header部分结束。
(3)Body:空行后面的内容都是Body。Body允许为空字符串。如果Body存在,则在Header中会有Content-Length属性来标识Body的长度。

4.2HTTP响应格式
(1)首行:[版本号]+[状态码]+[状态码解释]。
(2)Header:请求的属性,冒号分割的键值对,每组属性之间使用\r\n分隔,遇到空行表示Header部分结束。
(3)Body:空行后面的内容都是Body。Body允许为空字符串。如果Body存在,则在Header中会有一个Content-Length属性来标识Body的长度,如果服务器返回了一个html页面,那么html页面内容就是在body中。

4.3使用fiddler进行抓包

五、HTTP服务器的实现(随请求响应版本)
5.1代码及整体文件结构
对应的目录结构如下:

封装套接字(Sock.hpp文件):
#pragma once
#include <iostream>
#include <string>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include "Log.hpp"
const int backlog = 10;
extern log lg;
enum
{
SocketError = 2,
BindError,
ListenError
};
class Sock
{
public:
Sock()
: _sockfd(-1)
{
}
~Sock()
{
if (_sockfd >= 0)
{
close(_sockfd);
}
}
// 创建套接字
void Socket()
{
_sockfd = socket(AF_INET, SOCK_STREAM, 0);
if (_sockfd < 0)
{
lg.logmessage(Fatal, "socket fail, %s, %d", errno, strerror(errno));
exit(SocketError);
}
int opt = 1;
setsockopt(_sockfd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));
}
// 创建一个绑定接口
void Bind(const uint16_t port)
{
struct sockaddr_in local;
memset(&local, 0, sizeof(local));
local.sin_family = AF_INET;
local.sin_port = htons(port);
local.sin_addr.s_addr = INADDR_ANY;
if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0)
{
lg.logmessage(Fatal, "bind fail, %s, %d", errno, strerror(errno));
exit(BindError);
}
}
void Listen()
{
if (listen(_sockfd, backlog) < 0)
{
lg.logmessage(Fatal, "listen fail, %s, %d", errno, strerror(errno));
exit(ListenError);
}
}
int Accept(std::string *clientip, uint16_t *clientport)
{
struct sockaddr_in peer;
socklen_t len = sizeof(peer);
int newfd = accept(_sockfd, (struct sockaddr *)&peer, &len);
if (newfd < 0)
{
lg.logmessage(Warning, "accept fail, %s, %d", errno, strerror(errno));
return -1;
}
char ipstr[64];
inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));
// 获取远端主机的信息
*clientport = ntohs(peer.sin_port);
*clientip = ipstr;
return newfd;
}
bool Connect(const std::string &ip, const uint16_t &port)
{
struct sockaddr_in peer;
memset(&peer, 0, sizeof(peer));
peer.sin_family = AF_INET;
peer.sin_port = htons(port);
inet_pton(AF_INET, ip.c_str(), &peer.sin_addr);
int n = connect(_sockfd, (struct sockaddr *)&peer, sizeof(peer));
if (n == -1)
{
std::cerr << "connect to" << ip << ":" << port << std::endl;
return false;
}
return true;
}
void Close()
{
close(_sockfd);
}
int Fd()
{
return _sockfd;
}
private:
int _sockfd;
};
日志文件(Log.hpp文件):
#pragma once
#include <iostream>
#include <stdarg.h>
#include <time.h>
#include<sys/stat.h>
#include<sys/types.h>
#include<unistd.h>
#include<stdlib.h>
#include <fcntl.h>
#include<string.h>
#define SIZE 1024
// 设置日志等级
#define Info 0
#define Debug 1
#define Warning 2
#define Error 3
#define Fatal 4
#define Screen 1
#define Onefile 2
#define Classfile 3
#define logFile "log.txt"
class log
{
public:
log()
{
PrintMethod = Screen;
path = "./log/";
}
void Enable(int method)
{
PrintMethod = method;
}
std::string levelToString(int level)
{
switch (level)
{
case Info:
return "Info";
case Debug:
return "Debug";
case Warning:
return "Warning";
case Error:
return "Error";
default:
return "None";
}
}
void logmessage(int level, const char *format, ...) // 后面的省略号表示可变参数
{
char leftbuffer[SIZE];
time_t t = time(nullptr);
struct tm *ctime = localtime(&t);
snprintf(leftbuffer, sizeof(leftbuffer), "[%s],[%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),
ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday,
ctime->tm_hour, ctime->tm_min, ctime->tm_sec);
char rightbuffer[SIZE];
va_list s;
va_start(s, format);
vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);
va_end(s);
char logtxt[SIZE * 3];
snprintf(logtxt, sizeof(logtxt), "%s %s\n", leftbuffer, rightbuffer);
// printf("%d-%d-%d %d:%d:%d\n",ctime->tm_year + 1900, ctime->tm_mon, ctime->tm_mday, ctime->tm_hour,ctime->tm_min,ctime->tm_sec);
//printf("%s", logtxt);
PrintLog(level,logtxt);
// 格式:默认部分+自定义部分(可变参数部分)
}
void PrintLog(int level, const std::string& logtxt)
{
switch(PrintMethod)
{
case Screen:
std::cout << logtxt << std::endl;
break;
case Onefile:
printOneFile(logFile, logtxt);
break;
case Classfile:
printClassFile(level, logtxt);
break;
default:
break;
}
}
void printOneFile(const std::string &logname, const std::string &logtxt)
{
std::string _logname = path + logname;
int fd = open(_logname.c_str(),O_WRONLY|O_CREAT|O_APPEND,0666);
if(fd < 0)
{
return ;
}
write(fd,logtxt.c_str(),logtxt.size());
close(fd);
}
void printClassFile(int level, const std::string &logtxt)
{
std::string filename = logFile;
filename += ".";
filename += levelToString(level);
printOneFile(filename, logtxt);
}
~log()
{
}
private:
int PrintMethod;
std::string path;
};
log lg;
HTTP头文件(HTTPServer.hpp文件):
#pragma once
#include <iostream>
#include <string>
#include <sstream>
#include <pthread.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <fstream>
#include <unordered_map>
#include <vector>
#include "Socket.hpp"
#include "Log.hpp"
static const int defaultport = 8080;
class HttpServer;
const std::string wwwroot = "./wwwroot"; // web根目录
const std::string sep = "\r\n";
const std::string homepage = "idex.html";
struct ThreadData
{
public:
ThreadData(int fd, HttpServer *ts)
: sockfd(fd), httpsvr(ts)
{
}
public:
int sockfd;
HttpServer *httpsvr;
};
class HttpRequest
{
public:
void Deserialize(std::string req)
{
// 反序列化
while (true)
{
// 处理第一行(请求行)
std::size_t pos = req.find(sep);
if (pos == std::string::npos)
{
break;
}
std::string temp = req.substr(0, pos);
if (temp.empty())
{
break;
}
_req_header.push_back(temp);
req.erase(0, pos + sep.size());
// 继续对报头处理,获取正文长度,从而截取正文
// 。。。。
}
_text = req;
}
// 对序列化后的结果进行解析
void Parse()
{
// 以空格作为分隔符进行处理
std::stringstream ss(_req_header[0]);
ss >> method >> url >> http_version;
file_path = wwwroot;
if (url == "/" || url == "/idex.html")
{
file_path += "/";
file_path += homepage;
}
else
{
file_path += url;
}
auto pos = file_path.rfind(".");
if (pos == std::string::npos)
{
suffix = ".html";
}
else
{
suffix = file_path.substr(pos);
}
}
void DebugPrintf()
{
// 把报头打印出来
for (auto &line : _req_header)
{
std::cout << "-----------------------------------------------------------------" << std::endl;
std::cout << line << std::endl;
}
std::cout << _text << std::endl;
}
public:
std::vector<std::string> _req_header; // 请求行和请求报头
std::string _text; // 正文部分
// 对解析之后的结果进行定义
std::string method;
std::string url;
std::string http_version;
std::string file_path;
std::string suffix;
};
class HttpServer
{
public:
HttpServer(uint16_t port = defaultport)
: _port(port)
{
content_type.insert({".html", "text/html"});
content_type.insert({".png", "image/png"});
}
static std::string ReadHtmlContent(const std::string &htmlpath)
{
// 读文件操作
std::ifstream in(htmlpath, std::ios::binary);
if (!in.is_open())
{
return "";
}
//二进制读取
in.seekg(0, std::ios_base::end);
auto len = in.tellg();
in.seekg(0, std::ios_base::beg);
std::string content;
content.resize(len);
in.read((char *)content.c_str(), content.size());
//字符串读取
//std::string content;
//std::string line;
//while (std::getline(in, line))
//{
// content += line;
//}
in.close();
return content;
}
std::string SuffixToDesc(const std::string &suffix)
{
auto iter = content_type.find(suffix);
if (iter == content_type.end())
{
return content_type[".html"];
}
else
{
return content_type[suffix];
}
}
void HandleHTTP(int sockfd)
{
char buffer[4096];
ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);
if (n > 0)
{
buffer[n] = 0;
std::cout << buffer;
// 对读到的请求进行处理
HttpRequest req;
req.Deserialize(buffer);
req.Parse();
req.DebugPrintf();
// 返回响应
bool ok = true;
std::string test = ReadHtmlContent(req.file_path);
if (test.empty())
{
ok = false;
std::string err_html = wwwroot;
err_html += "/";
err_html += "err.html";
test = ReadHtmlContent(err_html);
}
std::string response_line;
if (ok == true)
{
response_line = "HTTP/1.1 200 OK\r\n";
}
else
{
response_line = "HTTP/1.1 404 Not Found\r\n";
}
std::string response_handler = "content_length:";
response_handler += std::to_string(test.size());
response_handler += "\r\n";
// 请求报头(添加数据类型)
response_handler += "Content_Type: ";
response_handler += SuffixToDesc(req.suffix);
response_handler += "\r\n";
response_handler += "Set-Cookie: name=tjp";
response_handler += "\r\n";
response_handler += "Set-Cookie: password=123456";
response_handler += "\r\n";
// 还要带一个空行
std::string block_line = "\r\n";
std::string response = response_line;
response += response_handler;
response += block_line;
response += test;
send(sockfd, response.c_str(), response.size(), 0);
}
close(sockfd);
}
static void *ThreadRun(void *args)
{
ThreadData *td = static_cast<ThreadData *>(args);
pthread_detach(pthread_self());
td->httpsvr->HandleHTTP(td->sockfd);
delete td;
return nullptr;
}
bool Start()
{
_listen_sock.Socket();
_listen_sock.Bind(_port);
_listen_sock.Listen();
for (;;)
{
std::string clientip;
uint16_t clientport;
int sockfd = _listen_sock.Accept(&clientip, &clientport);
if (sockfd < 0)
{
continue; // 忽略失败的连接请求
}
pthread_t tid;
ThreadData *td = new ThreadData(sockfd, this);
pthread_create(&tid, nullptr, ThreadRun, td);
}
}
~HttpServer()
{
}
private:
Sock _listen_sock;
uint16_t _port;
std::unordered_map<std::string, std::string> content_type;
};
HTTP服务器运行文件(HTTPServer.cc文件):
#include "HttpServer.hpp"
#include<iostream>
#include<memory>
int main( )
{
std::unique_ptr<HttpServer> svr(new HttpServer());
svr->Start();
return 0;
}
网页代码(idex.html文件):
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>爱心表白</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
overflow: hidden;
}
/* 爱心容器 */
.heart {
width: 150px;
height: 150px;
position: relative;
animation: beat 1s infinite alternate;
}
/* 爱心形状 */
.heart::before,
.heart::after {
content: '';
width: 75px;
height: 120px;
background-color: #ff4d6d;
border-radius: 50px 50px 0 0;
position: absolute;
top: 0;
}
.heart::before {
left: 75px;
transform: rotate(-45deg);
transform-origin: 0 100%;
}
.heart::after {
left: 0;
transform: rotate(45deg);
transform-origin: 100% 100%;
}
/* 跳动动画 */
@keyframes beat {
0% {
transform: scale(1);
}
100% {
transform: scale(1.15);
}
}
/* 表白文字 */
.text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 24px;
font-weight: bold;
z-index: 10;
text-shadow: 0 0 10px #ff4d6d, 0 0 20px #ff4d6d;
white-space: nowrap;
}
</style>
</head>
<body>
<div class="heart"></div>
<div class="text">hello world</div>
<a href="http://182.254.175.111:8080/love.html">点击跳转</a>
<a href="http://www.baidu.com">点击跳转</a>
</body>
</html>
err.html文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>404 - 页面未找到</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
font-family: "Microsoft YaHei", sans-serif;
}
body {
background-color: #f8f9fa;
display: flex;
justify-content: center;
align-items: center;
min-height: 100vh;
text-align: center;
padding: 20px;
}
.container {
max-width: 600px;
width: 100%;
}
.error-code {
font-size: 10rem;
font-weight: bold;
color: #dc3545;
margin-bottom: 20px;
}
.title {
font-size: 2rem;
color: #333;
margin-bottom: 15px;
}
.desc {
font-size: 1.1rem;
color: #666;
margin-bottom: 30px;
line-height: 1.6;
}
.back-btn {
display: inline-block;
padding: 12px 30px;
background-color: #007bff;
color: white;
text-decoration: none;
border-radius: 6px;
font-size: 1rem;
transition: background 0.3s;
}
.back-btn:hover {
background-color: #0056b3;
}
</style>
</head>
<body>
<div class="container">
<div class="error-code">404</div>
<h1 class="title">页面未找到</h1>
<p class="desc">抱歉,你访问的页面不存在或已被移动。<br>请检查网址是否正确,或返回首页继续浏览。</p>
<a href="/" class="back-btn">返回首页</a>
</div>
</body>
</html>
love.html文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>爱心表白</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
body {
min-height: 100vh;
display: flex;
justify-content: center;
align-items: center;
background-color: #000;
overflow: hidden;
}
/* 爱心容器 */
.heart {
width: 150px;
height: 150px;
position: relative;
animation: beat 1s infinite alternate;
}
/* 爱心形状 */
.heart::before,
.heart::after {
content: '';
width: 75px;
height: 120px;
background-color: #ff4d6d;
border-radius: 50px 50px 0 0;
position: absolute;
top: 0;
}
.heart::before {
left: 75px;
transform: rotate(-45deg);
transform-origin: 0 100%;
}
.heart::after {
left: 0;
transform: rotate(45deg);
transform-origin: 100% 100%;
}
/* 跳动动画 */
@keyframes beat {
0% {
transform: scale(1);
}
100% {
transform: scale(1.15);
}
}
/* 表白文字 */
.text {
position: absolute;
top: 50%;
left: 50%;
transform: translate(-50%, -50%);
color: #fff;
font-size: 24px;
font-weight: bold;
z-index: 10;
text-shadow: 0 0 10px #ff4d6d, 0 0 20px #ff4d6d;
white-space: nowrap;
}
</style>
</head>
<body>
<div class="heart"></div>
<div class="text">我爱你</div>
</body>
</html>
kiss.html文件:
<!DOCTYPE html>
<html lang="zh-CN">
<head>
<meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0">
<title>浪漫雪天 | 情侣接吻</title>
<style>
* {
margin: 0;
padding: 0;
box-sizing: border-box;
}
/* 页面整体样式 */
body {
min-height: 100vh;
background: linear-gradient(to bottom, #1a1a2e, #16213e); /* 深夜渐变背景 */
display: flex;
justify-content: center;
align-items: center;
overflow: hidden;
position: relative;
}
/* 雪花动画样式 */
.snow {
position: absolute;
top: 0;
left: 0;
width: 100%;
height: 100%;
pointer-events: none;
background-image:
radial-gradient(3px 3px, white, transparent),
radial-gradient(4px 4px, white, transparent);
background-size: 100px 100px, 80px 80px;
animation: snowFall 10s linear infinite;
}
/* 雪花下落动画 */
@keyframes snowFall {
0% { background-position: 0 0, 0 0; }
100% { background-position: 100px 1000px, 80px 800px; }
}
/* 动图容器 */
.gif-container {
z-index: 10;
border-radius: 20px;
box-shadow: 0 0 50px rgba(255, 255, 255, 0.3);
overflow: hidden;
}
/* 动图样式 */
.couple-gif {
width: 500px;
max-width: 90vw;
height: auto;
display: block;
}
</style>
</head>
<body>
<!-- 雪花背景 -->
<div class="snow"></div>
<!-- 情侣动图 -->
<div class="gif-container">
<img
class="couple-gif"
src="https://pic.ntimg.cn/file/20220112/10022400_171104684000_2.gif"
alt="雪天情侣接吻动图"
>
</div>
</body>
</html>
5.2运行结果

六、HTTP的方法

七、HTTP常见方法
7.1GET方法(重点)
用途:用于请求URL指定的资源
示例:GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。
注意:我们使用GET方法提交参数给服务器的时候,我们提交的参数是通过urI提交给服务器的
7.2POST方法
用途:用于传输实体的主体,通常用于提交表单数据。
示例:POST /submit.cgi HTTP/1.1
特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
注意:POST方法提交参数给服务器的时候,才通的是请求正文提交数据给服务器。
注意:POET方法相对GET方法更私密,但并不是更安全,GET方法提交参数会导致参数数量受限,而POST不会
八、HTTP状态码

最常见的状态码,比如200(OK)、404(Not Found)、403(Forbidden)、302(Redirect,重定向)、504(Bad Gateway)

九、重定向
重定向就是让服务器指导浏览器,让浏览器访问新的地址。

9.1HTTP状态码301(永久重定向)
(1)当服务器返回HTTP301状态码时,标识请求的资源已经被永久移动到新的位置。
(2)在这种情况下,服务器会在响应中添加一个Location头部,用于指定资源的新位置。这个Location头部包含了新的URL地址,浏览器会自动重定向到该地址。
例如,在HTTP响应中,可能会看到类似于以下的头部信息:

9.2HTTP状态码302(临时重定向)
(1)当服务器返回HTTP302状态码时,表示请求的资源临时被移动到新的位置。
(2)同样地,服务器也会在响应中添加一个Location头部来指定资源的新位置。浏览器会暂时使用新的URL进行后续的请求,但不会缓存这个重定向。
例如,在HTTP响应中,可能会看到类似于以下的头部信息:

十、HTTP常见Header
10.1常见的报头
Content-Type:数据类型(text/html等)
Content-Length:Body的长度
Host:客户端告知服务器,所请求的资源是在哪个主机的哪个端口上。
User-Agent:声明用户的操作系统和浏览器版本信息。
Referer:当前页面是从哪个页面跳转过来的。
Location:搭配3xx状态码使用,告诉客户端接下来要去哪里访问。
Cookie:用于在客户端存储少量信息。通常用于实现会话(session)的功能。
10.2connection报头
HTTP中的Connection字段是HTTP报文头的一部分,它主要用于控制和管理客戸端与服务器之间的连接状态。
10.2.1核心作用
(1)管理持久连接:Connection字段还用于管理持久连接(也称为长连接)。持久连接允许客户端和服务器。
在请求/响应完成后不立即关闭TCP连接,以便在同一个连接上发送多个请求和接收多个响应。
(2)持久连接(长连接)
HTTP/1.1:在HTTP/1.1协议中,默认使用持久连接。当客户端和服务器都不明确指定关闭连接时,连接将保持打开状态,以便后续的请求和响应可以复用同一个连接。
HTTP/1.0:在HTTP/1.0协议中,默认连接是非持久的。如果希望在HTTP/1.0上实现持久连接,需要在请求头中显式设置Connection: keep-alive。
10.2.2语法格式
Connection:keep-alive:表示希望保持连接以复用TCP连接
Connection:close:表示请求/响应完成后,应该关闭TCP连接。

十一、HTTP cookie
HTTP协议本身是无状态的,也就是HTTP不记录你上次是否访问过了。HTTP对登录用户的会话保持功能(HTTPCookie)HTTPCookie是服务器通过Set-Cookie响应头下发、浏览器本地存储并在后续请求自动带回的键值对小文本,用来让无状态的HTTP协议具备状态记忆能力。
11.1核心用途
会话管理:登录态、购物车、会话ID。
个性化:主题、语言、字体大小等偏好。
用户跟踪:行为分析、广告归因、跨站统计。
11.2工作流程
1、首次请求:浏览器发请求,无Cookie。
2、服务器下发:响应头带Set-Cookie,浏览器保存(按域名+路径隔离)。
3、后续请求:浏览器自动匹配域名/路径,在请求头Cookie中带回。
4、服务器识别:读取Cookie识别用户/会话,返回对应资源

11.3分类
(1)会话Cookie(Session Cookie):在浏览器关闭时失效
(2)持久Cookie(PersistentCookie):带有明确的过期日期或持续时间,可以跨多个浏览器会话存在
(3)如果cookie是一个持久性的cookie,那么它其实就是浏览器相关的,特定目录下的一个文件。但直接查看这些文件可能会看到乱码或无法读取的内容,因为cookie文件通常以二进制或sqlite格式存储。一般我们查看,直接在浏览器对应的选项中直接查看即可。
在浏览器中查看cookie:

11.4认识 cookie
(1)HTTP存在一个报头选项:Set-Cookie,可以用来进行给浏览器设置Cookie值。
(2)在HTTP响应头中添加,客户端(如浏览器)获取并自行设置并保存Cookie。
11.5基本格式

十二、HTTP Session
12.1定义
HTTPSession是服务器用来跟踪用户与服务器交互期间用户状态的机制。由于HTTP协议是无状态的(每个请求都是独立的),因此服务器需要通过Session来记住用户的信息。
12.2工作原理
当用户首次访问网站时,服务器会为用户创建一个唯一的SessionID,并通过Cookie将其发送到客户端。客户端在之后的请求中会携带这个SessionID,服务器通过SessionID来识别客户,从而获取用户的会话信息。服务器通常会将Session信息存储在内存、数据库或缓存中。
12.3安全性:
与Cookie相似,由于Session ID是在客戸端和服务器之间传递的,因此也存在被窃取的风险。但是一般虽然Cookie被盗取了,但是用户只泄漏了一个SessionID,私密信息暂时没有被泄露的风险SessionID便于服务端进行客户端有效性的管理,比如异地登录。可以通过HTTPS和设置合适的Cookie属性(如 HttpOnly和Secure)来增强安全性。
12.4超时和失效:
Session可以设置超时时间,当超过这个时间后,Session会自动失效。服务器也可以主动使Session失效,例如当用户登出时。
注意:SessionID是服务器统一分配管理的
openEuler 是由开放原子开源基金会孵化的全场景开源操作系统项目,面向数字基础设施四大核心场景(服务器、云计算、边缘计算、嵌入式),全面支持 ARM、x86、RISC-V、loongArch、PowerPC、SW-64 等多样性计算架构
更多推荐


所有评论(0)