当前位置: 首页 > news >正文

网络基础 【HTTP】

 💓博主CSDN主页:麻辣韭菜💓

⏩专栏分类:Linux初窥门径

🚚代码仓库:Linux代码练习🚚


💻操作环境: CentOS 7.6 华为云远程服务器


🌹关注我🫵带你学习更多Linux知识
  🔝 

目录

 1. HTTP协议

1.1 认识URL 

1.2 urlencode和urldecode 

1.3 协议格式

 2. 简易HTTP服务器

 2.1 见一见请求

 2.2 见一见响应

 2.2.1 路径解析

3. HTTP方法 

4. HTTP状态码 

5. HTTP常见Header

5.1 Content-Type

5.2 Cookie


 1. HTTP协议

 在前面我们讲了自己如何定制协议,但是我们自己定制的协议太简单了,我们的协议在应用层来说,根本不够用的,实际上,已经有大佬定义了一些现成的,又非常好用的应用层协议,比如本篇要讲解的HTTP协议(超文本传输协议)在学习HTTP之前我们需要先了解几个预备知识。

1.1 认识URL 

什么是URL?

我们平时所说的"网址",就是传说中的URL。

 我们在浏览器输入抖音的网址,就能访问抖音,可是我们平时并不知道抖音IP地址和端口号

为什么光输入一个域名就能访问了?

URL自动解析对应的IP地址       

而端口号是默认的,比如说HTTP 80 端口号,而HTTPS 443 端口号 

 如果我们没指明端口号,浏览器就会使用 协议 的默认端口

诸如上面的网址称为 URL -> Uniform Resource Locator 统一资源定位符,也就我们熟知的 超链接/链接URL 中包含了 协议、IP地址、端口号、资源路径、参数 等信息

上面的URL只有一个域名,其实还有,请看图

   注:user:pass 已经不用了,因为不安全。

 下面我以我个人博客主页来讲解 URL

 https://blog.csdn.net/2301_77934192?spm=1011.2266.3001.5343

1. 协议

  • https://:表示使用 HTTPS 协议进行安全的数据传输。HTTPS 是 HTTP 的安全版本,通过 SSL/TLS 加密数据,确保数据在传输过程中的安全性。

2. 域名

  • blog.csdn.net:这是 CSDN(中国软件开发网)的博客子域名。CSDN 是一个知名的技术社区,提供博客、论坛、问答等服务。

3. 路径

  • /2301_77934192:这是用户的唯一标识符或博客作者的 ID。这个部分通常指向特定用户的博客主页。

4. 查询参数

  • ?spm=1011.2266.3001.5343:这是 URL 的查询字符串,通常用于传递额外的信息给服务器。spm 是一个参数名,后面的值 1011.2266.3001.5343 可能用于跟踪来源、分析流量或其他目的。
  • :// 用于分隔 协议 和 IP地址
  • : 用于分隔 IP地址 和 端口号
  • / 表示路径,同时第一个 / 可以分隔 端口号 和 资源路径
  • ? 则是用来分隔 资源路径 和 参数

1.2 urlencodeurldecode 

/ ? : 等这样的字符 , 已经被 url 当做特殊意义理解了 . 因此这些字符不能随意出现 .
比如 , 某个参数中需要带有这些特殊字符 , 就必须先对特殊字符进行转义 .
转义的规则如下 :
将需要转码的字符转为 16 进制,然后从右到左,取 4 ( 不足 4 位直接处理 ) ,每 2 位做一位,前面加上 % ,编码成 %XY格式

比如

比如我们在百度搜索 C+++这个字符被转化成2B 

我们在上篇序列化与反序列化就是同样的道理。 下面是urlencode在线工具

 

1.3 协议格式

HTTP 协议 由 Request 请求 和  Response 响应 组成 有上篇的基础,我们就能大概知道 请求报文和响应报文的格式了。

从人类理解的角度来说:请求大概有这么几个部分组成。

请求行 :当中包括了请求的方法(GET POST),以及URL的协议版本(HTTP/1.0,TTTP/1.1,THHP/2.0) 

请求头:包含一系列键值对,提供了关于HTTP请求的附加信息,如: 

  • Host:指定请求的服务器的域名和端口号。
  • User-Agent:包含了发出请求的用户代理软件信息。
  • Accept:告知服务器客户端能够接收哪些类型的信息。
  • Accept-Language:告知服务器客户端能够接受的语言。
  • Accept-Encoding:告知服务器客户端能够接受的压缩格式。
  • Content-Type:当发送包含body的请求时,指定body的媒体类型。
  • Content-Length:当发送包含body的请求时,指定body的长度。
  • Connection:指定或要求服务器的连接状态。
  • Cookie:存储在用户本地的session信息。
  • Authorization:用于认证的信息。

空行:请求头和请求体之间的分隔符,通常是一个空行。

请求体/有效载荷:(可选)某些HTTP方法(如POST和PUT)可能会包含请求体,它包含了发送给服务器的数据。

 

 请求报文

POST /submit-form HTTP/1.1
Host: www.example.com
User-Agent: Mozilla/5.0 (Windows NT 10.0; Win64; x64)
Accept: text/html,application/xhtml+xml,application/xml;q=0.9,*/*;q=0.8
Accept-Language: en-US,en;q=0.5
Accept-Encoding: gzip, deflate
Content-Type: application/x-www-form-urlencoded
Content-Length: 27
Connection: keep-alivefield1=value1&field2=value2

对于响应 分为这么几个部分:

1. 状态行(Status Line)

状态行是HTTP响应报文的第一行,它包含以下三个部分:

  • HTTP版本(HTTP-Version):指定使用的HTTP协议的版本,如HTTP/1.1或HTTP/2。
  • 状态码(Status Code):一个三位数字,表示请求的结果,如200表示成功,404表示未找到,500表示服务器内部错误等。
  • 原因短语(Reason Phrase):一个简短的文本,用来提供状态码的额外信息。

2. 响应头(Response Headers)

响应头提供了关于响应的附加信息,它们是一系列的键值对。响应头也可以被分为几个不同的类别:

  • 通用头(General Headers):适用于所有类型的请求和响应,如Cache-ControlConnectionDate等。
  • 响应头(Response Headers):提供响应的附加信息,如ServerContent-TypeContent-Length等。
  • 实体头(Entity Headers):当响应包含响应体时使用,如Content-EncodingContent-LanguageContent-LocationContent-MD5Last-Modified等。

一些常见的响应头包括:

  • Server:包含了服务器软件的信息。
  • Content-Type:指定返回的资源的MIME类型。
  • Content-Length:指定返回的资源的长度。
  • Content-Encoding:指定了响应体的压缩格式。
  • Set-Cookie:用于设置客户端的cookie。
  • Last-Modified:指定资源的最后修改时间。
  • Cache-Control:指定响应的缓存指令。

3. 空行(Empty Line)

响应头和响应体之间的分隔符,通常是一个空行,表示响应头的结束。

4. 响应体(Response Body)

响应体是HTTP响应的一部分,它包含了服务器返回给客户端的数据。响应体的内容可以是HTML文档、图片、视频、JSON、XML等格式,具体取决于Content-Type响应头的值。

HTTP/1.1 200 OK
Date: Mon, 27 Sep 2024 12:28:53 GMT
Server: Apache/2.4.1 (Unix)
Last-Modified: Wed, 26 Sep 2024 12:28:53 GMT
Content-Length: 12345
Content-Type: text/html
Connection: close
ETag: "3f80f-1b6-3e1cb93b"<html>
<head><title>Example Response</title></head>
<body><h1>Hello, World!</h1>
</body>
</html>

 2. 简易HTTP服务器

 2.1 见一见请求

 我们编写一个服务器,利用浏览器作为客户端,浏览器通过 IP + Port 访问 我们编写的服务器,这时浏览器就会发出HTTP请求,浏览器接连到服务器后,服务器就会打印HTTP请求 

编写服务器所需要的文件:

 

log.hpp 和 Socket.hpp 和上篇的是一样的 直接拿过来用,自动化编译不用多说。 

先编写服务器

#pragma once
#include <iostream>
#include <string>
#include <thread>
#include "Socket.hpp"
static const uint16_t defaultport = 8080;class HttpServer;
class ThreadData
{
public:ThreadData(int sockfd, HttpServer *tpsvr): _sockfd(sockfd), _tpsvr(tpsvr) {}public:int _sockfd;HttpServer *_tpsvr; //回调指针
};
class HttpServer
{
public:HttpServer(uint16_t port = defaultport): _port(port) {}void Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();}static void ThreaRun(ThreadData* td){//先简单处理int sockfd = td->_sockfd;char buffer[10240];int n = recv(sockfd,buffer,sizeof(buffer),0);if(n > 0){buffer[n] = 0;std:: cout << buffer<<std::endl;}}void Start(){for (;;){std::string clientip;uint16_t clientport;int sockfd = _listensock.Accept(&clientip, &clientport);if (sockfd < 0)continue;lg(Info, "get a new link, clientip: %s, clientport: %d", clientip.c_str(), clientport);//创建线程处理请求ThreadData* td = new ThreadData(sockfd, this);std::thread t(ThreaRun, td);t.detach();}}private:Sock _listensock;uint16_t _port;
};

编写主函数 

#include "HttpServer.hpp"
#include <iostream>
#include <memory>int main()
{std::unique_ptr<HttpServer> svr(new HttpServer());svr->Init();svr->Start();return 0;
}

make 一下 编译通过后,运行HttpServer可执行程序。

 

通过指令 netstat 看到服务器已经运行了 ,这时我们在浏览器输入IP+port 服务器就会打印请求消息。 

 

 没有页面也很正常 我们服务器还没有写业务函数来进行响应。

从请求行来看 请求的方法为 GET  版本为HTTP/1.1 请求路径为 / (根目录)如果我们指定路径访问,则会直接访问该指定路径。 

从这个两个请求报文来看 服务器可以识别是什么类型的设备在请求链接 也就是User-Agent

我们用爬虫,有时候爬不了的原因就在这里,HTTP根据User-Agent 如果是非法的用户(也就是报文的格式不对)User-Agent  或者根本就没有,那么直接就不给响应了。这就是反爬策略。

User-Agent 还有作用就是:比如我们在网站上下载东西时,下载的软件是直接对应你机器的操作系统。

比如 我要下载微信 点进去的下载链接 ,直接就是windows电脑版

 

2.2 见一见响应

static void ThreaRun(ThreadData *td){// 先简单处理int sockfd = td->_sockfd;char buffer[10240];int n = recv(sockfd, buffer, sizeof(buffer), 0);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;// 返回响应std::string text = "Hello World";                  // 响应的内容std::string response_line = "HTTP/1.0 200 OK\r\n"; // 响应行std::string response_header = "Content-Length:";   // 响应报文response_header += std::to_string(text.size());    // 内容的长度response_header += "\r\n";std::string bank_line = "\r\n"; // 空行std::string response = response_line;response += response_header;response += bank_line;response += text;// 发送报文send(td->_sockfd, response.c_str(), response.size(), 0);}}

 通过简单代码我们将字符串 "Hello World" 拼接到响应报文正文部分,发送给客户端(浏览器),而浏览器通过解释,最终在界面上显示了 Hello World。 这也就对应我们前面的讲的响应报文里面有效载荷。

 2.2.1 路径解析

其实我们还可以通过URL访问指定文件,就比如下面文件abc,也是说HTTP网络文件有很多,比如图片、视频、音频、JS文件、样式文本等。那么HTTP一定就会有一个web根目录如同Linux的根目录。 

前面代码很挫,如果我们要更改网站的样式,每次我们都要静态的写入到我们服务器中,所以我们可以创建一个文件,将htlm写入到这个文件中,下次再改就不用改服务器了。 

 基于刚才讲的 我们直接就在进程当前目录创建一个文件夹 wwwroot 以后网站首页也好,图片视频也罢 直接就从这个wwwroot根目录访问

 所以这段代码就不能这么写了。我们重新写一个类 HTTP请求的类,然后对请求做反序列化,拿到url 。

const std::string wwwroot = "./wwwroot";
const std::string sep = "\r\n";
const std::string homepage = "index.html";class HttpRequest
{
public:// 进行反序列化void Deserialization(std::string req){size_t pos = 0;while ((pos = req.find(sep)) != std::string::npos){size_t next_pos = pos + sep.size();if (pos > 0){ // 确保不是空字符串req_header.push_back(req.substr(0, pos));}req.erase(0, next_pos);}// 循环退出后,剩下的就是报文的正文部分text = req;}//解析请求行void Parse(){std::stringstream ss(req_header[0]);ss >> method >> url >> http_version;file_path = wwwroot;if (url == "/" || url == "/index.html") // 访问根目录 就只返回网站首页{file_path += "/";file_path += homepage;}elsefile_path += url; // 访问其他路径}void DebugPrint(){for (auto &line : req_header){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method: " << method << std::endl;std::cout << "url: " << url << std::endl;std::cout << "http_version: " << http_version << std::endl;std::cout << "file_path: " << file_path << 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;
};

 在我们当前目录 新建wwwroot目录 然后再这个目录下创建 index.html

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8">
<meta name="viewport" content="width=device-width, initial-scale=1.0"><title>Document</title>
</head>
<body><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1><h1>Hello world</h1>
</body>
</html>

在HttpServer这个类中编写下面函数

// 根据解析的路径确定打开那个文件static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath); // 这里只能打开字符串文件,图片不行。if (!in.is_open()){return "404";}std::string content;std::string line;while (std::getline(in, line)){content += line;}in.close();return content;}

在原来的TreadRun进行变形得到我们想要效果 

static void ThreaRun(ThreadData *td){// 先简单处理int sockfd = td->_sockfd;char buffer[10240];int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;// std::cout << buffer << std::endl;HttpRequest req;req.Deserialization(buffer);req.Parse();req.DebugPrint();// 返回响应std::string text = ReadHtmlContent(req.file_path); // 响应的内容std::string response_line = "HTTP/1.0 200 OK\r\n"; // 响应行std::string response_header = "Content-Length:";   // 响应报文response_header += std::to_string(text.size());    // 内容的长度response_header += "\r\n";std::string bank_line = "\r\n"; // 空行std::string response = response_line;response += response_header;response += bank_line;response += text;// 发送报文send(td->_sockfd, response.c_str(), response.size(), 0);}delete td;}

这里我们读是有bug的 这里我们就假设读到的是一个完整的报文。 

 

这样做的好处就是,我们访问网站首页,就只会返回网站首页,而不会返回根目录下的所有内容。 同理访问其他的路径也是一样。

这时我们再在wwwroot 创建a b c 文件夹 分别在这3个目录中创建 html文件

 

 我们添加链接就可以跳转 其他网页

 这里前端知识我们不细说,感兴趣的可以去w3school 在线教程 看看

 点击就跳转到 第二个网页

 这还是要得益于我们对请求请求行反序列化,然后将URL提取出来,在服务器中路径解析 找到目录 打开文件。

3. HTTP方法 

通过前面的演示,服务器打印的请求都是GET方法,也就是说我们要获取服务器的某个资源基本用的都是GET方法。

 那POST也可获取,那POST与GET获取有什么不同? 

不要忘记了 ,我们作为客户端除了请求服务器的资源,也是可以向服务器提交数据的。 就比如我们在百度搜索东西时,搜索关键字linux 提交给百度服务器。

 再比如登陆gitee 网站 用户信息 也是数据

基于前面的认识之后 我们再来谈谈为什么有了GET 还要有POST?

首先GET的数据传输是通过URL的,URL本身就有长度限制,那么就意味着GET请求传输的数据长度有限。

其次 数据 在URL 中本身是可见,一些敏感信息就不适合用URL传输,就比如用户账号信息。

最后 URL请求可以被缓存,那么我们传递数据就会被浏览器保存,被第三方看到。 

一句话 总结就是:GET方法传输数据不安全。

POST方法: 

  1. 数据传输:通过请求体(Request Body)传递数据,数据不会出现在地址栏中。
  2. 数据长度限制:POST请求没有数据长度限制。
  3. 缓存:POST请求不会被缓存。
  4. 历史记录:POST请求不会保存在浏览器的历史记录中。
  5. 可见性:数据不会在URL中显示,因此相对更安全。
  6. 用途:适合向服务器提交数据。
  7. 方式:数据被包含在请求体中,可以传输更复杂的数据类型。

总结

  • GET 主要用于请求服务器发送数据。
  • POST 主要用于向服务器提交数据。

当然POST 提交的 数据不安全。因为HTTP协议都是明文传送的。

 那数据是怎么样提交给服务器的?

 

 在前端来说这个叫表单,我们的数据都是通过表单来提交的!

后面的方法要被HTTP禁用,要么就是随着时代发展被淘汰了不用了。我们在HTTP中用到的方法 95%以上用的是 GET 和  POST。 

基于这么我们先用GET 方法做实验 在HTML 表单 (w3school.com.cn) 前端代码拿过来直接用。

 点击登陆后,跳转网页后 地址框URL如下面所示

 从这个图片我们可以看到 用户 是zhangsan 密码 123456。 这也验证了 我们前面的讲的GET方法提交数据不安全。

从这个URL看 以为分隔符,前面的如果是个可执行程序 而后面是参数。那么我们就可以创建子进程 做程序替换而这个程序替换可以是登陆认证,插入数据库,搜索等。

 我们改成post方法 参数通过了请求体(正文)传输。

4. HTTP状态码 

这里100开头和200开头没什么好说的,我们在写响应的时候 就是 200 OK 标识成功,我们再说400开头的。

 我们访问百度 通过URL指定访问路径a/b/c出现了下面的界面

 也就是传说中404 你访问的页面不存在。基于这样我们也可以写一个err.html。毕竟这个世界上的服务器不可能搜集到所有资源,客户端访问的东西我们没有,但是也要响应。

那么前面的代码我们就要改一改

static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath); // 这里只能打开字符串文件,图片不行。if (!in.is_open()){return ""; //之前返回404 现在返回空串}

响应报文对应也要改一改

            std::string text = ReadHtmlContent(req.file_path); // 响应的内容bool ok = true;if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/err.html";text = ReadHtmlContent(err_html);}std::string response_line;if (ok)response_line = "HTTP/1.0 200 OK\r\n"; // 响应行elseresponse_line = "HTTP/1.0 404 Not Found\r\n";

这时我们在wwwroot目录下添加err.html文件,404前端代码 网上随便找一个过来CV一下。

 源代码我在网上找了一个,cv过来 ,现在我们运行试试

 对于5开头的,那一般都是服务器的问题,配置出错了,资源出错了等。我们还有有一个3开头的状态码没有说

300开头的叫做重定向 一般有两种 一种 302 临时重定向 一种是 301永久重定向。

说人话那就是说 原本我们访问的是我们的网站,结果访问的是其他网站。

那什么时候用临时?

不知道大家登陆认证的时候,是不是跳转了其他页面,而这个页面就是临时重定向。

永久不用多说了,以前网站老化,不用了。跳转到新的网站

下面我对报文进行变形 改成重定向 

   static void ThreaRun(ThreadData *td){// 先简单处理int sockfd = td->_sockfd;char buffer[10240];int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;HttpRequest req;req.Deserialization(buffer);req.Parse();//req.DebugPrint();// 返回响应std::string text = ReadHtmlContent(req.file_path); // 响应的内容bool ok = true;if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/err.html";text = ReadHtmlContent(err_html);}std::string response_line;if (ok)response_line = "HTTP/1.0 200 OK\r\n"; // 响应行elseresponse_line = "HTTP/1.0 404 Not Found\r\n";response_line = "HTTP/1.0 302 Found\r\n"; //重定向std::string response_header = "Content-Length:"; // 响应报文response_header += std::to_string(text.size());  // 内容的长度response_header += "\r\n";response_header += "Location: https://www.baidu.com\r\n";//重定向到百度std::string bank_line = "\r\n"; // 空行std::string response = response_line;response += response_header;response += bank_line;response += text;// 发送报文send(td->_sockfd, response.c_str(), response.size(), 0);}delete td;}

 

 5. HTTP常见Header

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

 除了Content-Type Cookie 没有讲 前面内容都讲过了。  

5.1 Content-Type

在讲 Content-Type 之间 我们需要先了解Connection

打开B站首页 感觉是我们只访问首页,也就是和服务器进行一次请求和响应。其实不然,B站首页有许多图片和视频, 这些也是资源,其实服务器会给我们多次响应,多次取决于有多少个资源。

在上古时代也就是 HTTP/1.0的时代,客户端和服务器连接都是短连接,比较那个时候网页内容不多。所以Hold的住,但是现在还是采用1.0那就不行了,毕竟现在一个网页就有几百张图片,浏览器和服务器之间就得建立几百次连接。效率低下 

现在都是HTTP/1.1时代,也就说长连接 一次连接返回你要访问的所有资源 

我们前面所写网站可是没有图片的,那如何添加图片?需要注意的是文本不同于图片和视频

他们都有对照表

也就说服务器要知道我们在请求什么资源,需要知道它的类型,根据 请求报文的 Content-Type

注明 服务器知道了是什么类型的资料 根据对照表 在响应报文中添加字段发给浏览器。  

 HTTP content-type 对照表

 所以基于这样 我们需要对之前代码继续变形

变形1:由于有对照表,所以我们需要unordered_map 用来存放 资源类型和它的对照表。

变形2:在原来的Parse()函数中 ,要解析出 资源的类型。

变形3: ReadHtmlContent()函数中 以前是读文本,但是图片和视频是二进制的,以前的读法就不行了,改为二进制来读。 

改造后代码 

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include <fstream>
#include <thread>
#include "Socket.hpp"const std::string wwwroot = "./wwwroot";
const std::string sep = "\r\n";
const std::string homepage = "index.html";
const std::string contentype = "./wwwroot/content_type.txt";
const std::string sep1 = ":";
static const uint16_t defaultport = 8080;class HttpServer;
class ThreadData
{
public:ThreadData(int sockfd, HttpServer *tpsvr): _sockfd(sockfd), _tpsvr(tpsvr) {}public:int _sockfd;HttpServer *_tpsvr; // 回调指针
};class HttpRequest
{
public:// 进行反序列化void Deserialization(std::string req){size_t pos = 0;while ((pos = req.find(sep)) != std::string::npos){size_t next_pos = pos + sep.size();if (pos > 0){ // 确保不是空字符串req_header.push_back(req.substr(0, pos));}req.erase(0, next_pos);}// 循环退出后,剩下的就是报文的正文部分text = req;}// 解析请求行void Parse(){std::stringstream ss(req_header[0]);ss >> method >> url >> http_version;file_path = wwwroot;if (url == "/" || url == "/index.html") // 访问根目录 就只返回网站首页{file_path += "/";file_path += homepage;}elsefile_path += url; // 访问其他路径auto pos = file_path.rfind("."); // 找路径文件后缀格式if (pos == std::string::npos){suffix = ".htlm";}else{suffix = file_path.substr(pos); // 找到了,文件后缀格式放在容器中}}void DebugPrint(){for (auto &line : req_header){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method: " << method << std::endl;std::cout << "url: " << url << std::endl;std::cout << "http_version: " << http_version << std::endl;std::cout << "file_path: " << file_path << 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) {}void Init(){_listensock.Socket();_listensock.Bind(_port);_listensock.Listen();std::ifstream in(contentype);if (!in.is_open()){lg(Fatal, "isfstream open error %s", contentype.c_str());exit(1);}std::string line;while (std::getline(in, line)){std::string part1, part2;Split(line, &part1, &part2);content_type.insert({part1, part2});}in.close();}// 将content_type.txt 分割成 哈希键值对 后序插入bool Split(const std::string &s, std::string *part1, std::string *part2){auto pos = s.find(sep1);if (pos == std::string::npos)return false;*part1 = s.substr(0, pos);*part2 = s.substr(pos + 1);return true;}// 根据解析的路径确定打开那个文件static std::string ReadHtmlContent(const std::string &htmlpath){std::ifstream in(htmlpath, std::ios::binary); // 按二进制打开if (!in.is_open()){return "";}std::string content;in.seekg(0, std::ios::end); // 找到文件的最后位置auto len = in.tellg();      // 算出文件的长度in.seekg(0, std::ios::beg); // 文件最后位置复位content.resize(len);in.read((char *)content.c_str(), content.size());// std::string line;// while (std::getline(in, line))// {//     content += line;// }in.close();return content;}static void ThreaRun(ThreadData *td){// 先简单处理int sockfd = td->_sockfd;char buffer[10240];int n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;std::cout << buffer << std::endl;HttpRequest req;req.Deserialization(buffer);req.Parse();// req.DebugPrint();//  返回响应std::string text = ReadHtmlContent(req.file_path); // 响应的内容bool ok = true;if (text.empty()){ok = false;std::string err_html = wwwroot;err_html += "/err.html";text = ReadHtmlContent(err_html);}std::string response_line;if (ok)response_line = "HTTP/1.0 200 OK\r\n"; // 响应行elseresponse_line = "HTTP/1.0 404 Not Found\r\n";// response_line = "HTTP/1.0 302 Found\r\n"; //重定向std::string response_header = "Content-Length:"; // 响应报文response_header += std::to_string(text.size());  // 内容的长度response_header += "\r\n";response_header += "Content-Type:";response_header += td->_tpsvr->SuffixToDesc(req.suffix);response_header += "\r\n";// response_header += "Location: https://www.baidu.com\r\n";//重定向到百度std::string bank_line = "\r\n"; // 空行std::string response = response_line;response += response_header;response += bank_line;response += text;// 发送报文send(td->_sockfd, response.c_str(), response.size(), 0);}delete td;}std::string SuffixToDesc(const std::string &suffix){auto iter = content_type.find(suffix);if (iter == content_type.end())return content_type[".html"];elsereturn content_type[suffix];}void Start(){for (;;){std::string clientip;uint16_t clientport;int sockfd = _listensock.Accept(&clientip, &clientport);if (sockfd < 0)continue;lg(Info, "get a new link, clientip: %s, clientport: %d", clientip.c_str(), clientport);// 创建线程处理请求ThreadData *td = new ThreadData(sockfd, this);std::thread t(ThreaRun, td);t.detach();}}private:Sock _listensock;uint16_t _port;std::unordered_map<std::string, std::string> content_type;
};

 5.2 Cookie

 你在B站 或者 腾讯视频、爱奇艺等网站,只要登陆认证了一次后,下次再访问时就不会出现登陆

这是因为Cookie的作用。

当我扫码登陆之后浏览器里面就有一个配置文件Cookie文件 当我们下次访问B站时,浏览器就会带着Cookie文件一起发送给服务器。而这个Cookie文件中包含了用户名 和 密码 。所以下次我们访问VIP资源时就不需要登陆认证了。这个现象我们叫做 会话保持

 当然 Cookie 文件也有内存级文件级 而我们上面的就是内存级,到期时间是浏览会话结束。

代码层面我们也演示

 

 

当我们讲了Cookie 你就应该意识到 这个保存用户信息的文件它是不安全的,一些木马程序扫描你电脑里的Cookie文件。找到了就拿走,就不就是传说中盗号吗?而且个人私密信息也被拿走了

基于这样的安全问题。后面服务端搞了一个sessionID

但是sessionID就安全了吗? 答案是不安全。

为什么这么说 因为Cookie文件还是在浏览器中,没有sessionID以前是客户自己保留私密信息,有了sessionID以后交给了服务器。现在用户的私密信息交给了服务端来维护了。也就说个人私密信息盗不走了,但是Cookie里面的sessionID别人还是能够拿到。

服务器就可以制定安全策略 识别是否为异常登录:

  • IP比对:识别登录用户的IP在短时间内是否发生了改变
  • 设备对比:不是本人常用设备 

如果发现异常登陆 直接就把sessionID 的状态设置为暂停状态,客户再访问时需要进行登陆认证,认证失败,服务器直接就删除sessionID. 

相关文章:

网络基础 【HTTP】

&#x1f493;博主CSDN主页:麻辣韭菜&#x1f493;   ⏩专栏分类&#xff1a;Linux初窥门径⏪   &#x1f69a;代码仓库:Linux代码练习&#x1f69a; &#x1f4bb;操作环境&#xff1a; CentOS 7.6 华为云远程服务器 &#x1f339;关注我&#x1faf5;带你学习更多Linux知识…...

[Linux#61][UDP] port | netstat | udp缓冲区 | stm32

目录 0. 预备知识 1. 端口号的划分范围 2. 认识知名端口号 3. netstat 命令 4. pidof 命令 二.UDP 0.协议的学习思路 1. UDP 协议报文格式 报头与端口映射&#xff1a; 2. UDP 的特点 面向数据报&#xff1a; 3. UDP 的缓冲区 4. UDP 使用注意事项 5. 基于 UDP 的…...

定义类方法的错误总结

struct Renderer {vector<function<void(vector<string>)>> fileDropListeners;// 定义一个方法&#xff0c;它是将一个函数作为输入&#xff0c;callback是形参void print(function<void(float)> callback_func);void onFileDrop(function<void(ve…...

Redis --- 第三讲 --- 通用命令

一、get和set命令 Redis中最核心的两个命令 get 根据key来取value set 把key和value存储进去 redis是按照键值对的方式存储数据的。必须要先进入到redis客户端。 语法 set key value &#xff1a; key和value都是字符串。 对于上述这里的key value 不需要加上引号&#…...

【Linux】进程间关系与守护进程

超出能力之外的事&#xff0c; 如果永远不去做&#xff0c; 那你就永远无法进步。 --- 乌龟大师 《功夫熊猫》--- 进程间关系与守护进程 1 进程组2 会话3 控制终端4 作业控制5 守护进程 1 进程组 之前我们提到了进程的概念&#xff0c; 其实每一个进程除了有一个进程 ID(P…...

【可视化大屏】将柱状图引入到html页面中

到这里还是用的死数据&#xff0c;先将柱状图引入html页面测试一下 根据上一步echarts的使用步骤&#xff0c;引入echarts.js后需要初始化一个实例对象&#xff0c;所以新建一个index.js文件来进行创建实例化对象和配置数据信息等。 //在index.html引入<script src"j…...

gm/ID设计方法学习笔记(一)

前言&#xff1a;为什么需要gm/id &#xff08;一&#xff09;主流设计方法往往侧重于强反型区&#xff08;过驱>0.2V&#xff09;&#xff0c;低功耗设计则侧重于弱反型区&#xff08;<0&#xff09;&#xff0c;但现在缺乏对中反型区的简单和准确的手算模型。 1.对于…...

高度细化的SAGA模式实现:基于Spring Boot与RabbitMQ的跨服务事务

场景与技术栈 场景&#xff1a;电商系统中的订单创建流程&#xff0c;涉及订单服务&#xff08;Order Service&#xff09;、库存服务&#xff08;Inventory Service&#xff09;、支付服务&#xff08;Payment Service&#xff09;。 技术栈&#xff1a; Java 11 Spring Bo…...

Vue工程化开发

Vue工程化开发 一、工程化开发和脚手架 1.开发Vue的两种方式 核心包传统开发模式&#xff1a;基于html / css / js 文件&#xff0c;直接引入核心包&#xff0c;开发 Vue。工程化开发模式&#xff1a;基于构建工具&#xff08;例如&#xff1a;webpack&#xff09;的环境中开…...

Ray_Tracing_The_Next_Week下

5image Texture Mapping 图像纹理映射 我们之前虽然在交点信息新增了uv属性&#xff0c;但其实并没有使用&#xff0c;而是通过p交点笛卡尔坐标确定瓷砖纹理或者大理石噪声纹理的值 现在通过uv坐标读取图片&#xff0c;通过std_image库stbi_load&#xff08;path&#xff09;…...

ES索引生命周期管理

基于如何 定时删除ES索引过期数据 而引发的一系列关于ES索引生命周期管理ILM(Index Lifecycle Management)的学习 快速上手 &#xff1a;定时删除ES索引中的过期数据 1. ILM解决什么问题&#xff1f; ES从6.7版本引入ILM&#xff0c;通过ILM可以解决哪些问题呢? 自动新建…...

Oracle数据库体系结构基础

关于Oracle体系结构 基于Oracle11g体系结构 目标&#xff1a; 了解Oracle体系结构掌握逻辑存储结构掌握物理存储结构熟悉Oracle服务器结构熟悉常用的数据字典 Oracle数据库管理中的重要的三个概念 实例&#xff08;instance):实例是指一组Oracle后台进程以及在服务器中分配…...

QT学习笔记4.5(文件、参数文件)

QT学习笔记4.5&#xff08;文件、参数文件&#xff09; 1.保存配置参数 1.使用QSettings保存到注册表&#xff0c;ini文件 2.文件存储&#xff1a;使用 QFile 和其他类将参数保存到文本文件、二进制文件、XMLWENJIAN、JSON 文件等。 文本文件&#xff1a;以简单的键值对格式…...

服务器虚拟化的详细学习要点

服务器虚拟化的详细学习要点可以归纳为以下几个方面: 1. 基本概念与原理 定义与原理:了解服务器虚拟化是一种将物理服务器资源转化为虚拟服务器资源的技术,允许在一台物理服务器上运行多个虚拟服务器。 虚拟化层次:理解虚拟化的不同层次,如裸机虚拟化(Type 1)和托管虚…...

创建一个Java Web API项目

创建一个Java Web API涉及多个步骤和技术栈&#xff0c;包括项目设置、依赖管理、数据访问层实现、业务逻辑实现、控制层开发以及测试和部署。在这篇详解中&#xff0c;我将带领你通过一个完整的Java Web API实现流程&#xff0c;采用Spring Boot和MyBatis-Plus作为主要技术工具…...

对称加密算法的使用Java和C#

1. JAVA中的使用 1.1.原生使用 Main函数代码 import symmetric_encryption.AESExample; import symmetric_encryption.BlowfishExample; import symmetric_encryption.DESExample; import symmetric_encryption.TripleDESExample; public class App { public static…...

10款好用的开源 HarmonyOS 工具库

大家好&#xff0c;我是 V 哥&#xff0c;今天给大家分享10款好用的 HarmonyOS的工具库&#xff0c;在开发鸿蒙应用时可以用下&#xff0c;好用的工具可以简化代码&#xff0c;让你写出优雅的应用来。废话不多说&#xff0c;马上开整。 1. efTool efTool是一个功能丰富且易用…...

ubuntu22.04中备份Iptables的设置

在 Ubuntu 22.04 中备份 iptables 的设置&#xff0c;您可以采用以下几种方法&#xff1a; 使用 iptables-save 命令&#xff1a; 您可以使用 iptables-save 命令将当前的 iptables 规则保存到文件中。例如&#xff0c;要将规则保存到 /etc/iptables/rules.v4 文件中&#xff0…...

(PyTorch) 深度学习框架-介绍篇

前言 在当今科技飞速发展的时代&#xff0c;人工智能尤其是深度学习领域正以惊人的速度改变着我们的世界。从图像识别、语音处理到自然语言处理&#xff0c;深度学习技术在各个领域都取得了显著的成就&#xff0c;为解决复杂的现实问题提供了强大的工具和方法。 PyTorch 是一个…...

若依从redis中获取用户列表

因为若依放入用户的时候&#xff0c;会在减值中添加随机串&#xff0c;所以用户的key会在redis中变成&#xff1a; login_tokens:6af07052-b76d-44dd-a296-1335af03b2a6 这样的样子。 如果用 Set<Object> items redisService.redisTemplate.keys("login_tokens&…...

文件上传之%00截断(00截断)以及pikachu靶场

pikachu的文件上传和upload-lab的文件上传 目录 mime type类型 getimagesize 第12关%00截断&#xff0c; 第13关0x00截断 差不多了&#xff0c;今天先学文件上传白名单&#xff0c;在网上看了资料&#xff0c;差不多看懂了&#xff0c;但是还有几个地方需要实验一下&#…...

Chainlit集成LlamaIndex并使用通义千问实现和数据库交互的网页对话应用(text2sql)

前言 我在之前的几篇文章中写了如何使用Chainlit集成Langchain并使用通义千问实现和数据库交互的网页对话应用&#xff0c;但是发现Langchain的几种和数据库交互的组件都不够让我满意&#xff0c;虽然已经满足了大部分场景的需求&#xff0c;但是问题还是很多&#xff0c;比如…...

计组复习笔记

计组笔记 汇编部分 通用寄存器&#xff08;General Registers&#xff09;: AX (Accumulator): 用于累加运算&#xff0c;也是乘法和除法的默认寄存器。BX (Base Register): 可以用作一个基址寄存器&#xff0c;通常用于存放数据的基地址。CX (Counter Register): 通常用于循环…...

62. 环境贴图2

环境贴图作用测试 实际生活中光源照射到一个物体上&#xff0c;这个物体反射出去的光线也会影响其他的物体&#xff0c;环境贴图就是用一种简单方式&#xff0c;近似模拟一个物体周边环境对物体表面的影响。 测试&#xff1a;对于PBR材质&#xff0c;如果threejs三维场景不添…...

MATLAB中数据导入与导出的全面指南

在MATLAB中&#xff0c;数据的导入与导出是数据处理工作流中的两个基本步骤。导入是将外部数据加载到MATLAB工作区的过程&#xff0c;而导出则是将工作区中的数据保存到外部文件中。这两个步骤对于数据分析、可视化和结果共享至关重要。本文将详细介绍如何在MATLAB中进行数据的…...

Jenkins从入门到精通,构建高效自动化流程

目录 一、Jenkins简介1、Jenkins的历史与发展&#xff08;1&#xff09;Jenkins的起源&#xff08;2&#xff09;Jenkins的发展&#xff08;3&#xff09;Jenkins的社区与生态系统&#xff08;4&#xff09;Jenkins在我国的发展 2、Jenkins的核心功能3、Jenkins的应用场景 二、…...

【Android 13源码分析】Activity生命周期之onCreate,onStart,onResume-2

忽然有一天&#xff0c;我想要做一件事&#xff1a;去代码中去验证那些曾经被“灌输”的理论。                                                                                  – 服装…...

如何在电脑上浏览手机界面

联想浏览器中&#xff0c;点击右键-》检查&#xff0c;进入开发者工具&#xff1a; 点击如上&#xff0c;红色框框选中的手机浏览模式即可。...

国产RISC-V案例分享,基于全志T113-i异构多核平台!

RISC-V核心优势 全志T113-i是一款双核Cortex-A7@1.2GHz国产工业级处理器平台,并内置玄铁C906 RISC-V和HiFi4 DSP双副核心,可流畅运行Linux系统与Qt界面,并已适配OpenWRT系统、Docker容器技术。 而其中的RISC-V属于超高能效副核心,主频高达1008MHz,标配内存管理单元,可运…...

Day(16)--File

File File对象就是表示一个路径&#xff0c;可以是文件路径&#xff0c;也可以是文件夹的路径 这个路径可以是存在的&#xff0c;也允许是不存在的 常见的方法 public File(String pathname);//根据文件路径创建文件对象 public File(String parent,String child);//根据父路…...