【计算机网络】应用层协议Http——构建Http服务服务器

article/2025/8/2 23:01:21

🔥个人主页🔥:孤寂大仙V
🌈收录专栏🌈:计算机网络
🌹往期回顾🌹: 【Linux笔记】——进程间关系与守护进程
🔖流水不争,争的是滔滔不息


  • 一、Http协议
    • URL
    • urlencode 和 urldecode
    • HTTP协议请求与响应格式
    • HTTP常见方法
    • Http状态码
      • 关于重定向
    • HTTP常见Header
  • 二、构建http服务器
    • Util.hpp 工具类
    • Http.hpp应用层Http协议的代码
      • **HttpRequest 处理客户端http请求**
      • HttpRespose.hpp服务端做应答
      • Http类最核心调度逻辑
    • **main.cc**
  • 三、Cookie和Session

一、Http协议

HTTP(HyperText Transfer Protocol,超文本传输协议)是一种用于分布式、协作式和超媒体信息系统的应用层协议。它是万维网(WWW)数据通信的基础,设计用于客户端与服务器之间的请求-响应交互。
HTTP 协议是客户端与服务器之间通信的基础。 客户端通过 HTTP 协议向服务器发送请求, 服务器收到请求后处理并返回响应。 HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。

URL

http://www.example.com:8080/index.html

我们平常所俗称的“网址”就是说的URL。上面的url,https是采用的协议,www.example.com是域名也就是ip,端口一般是:后面的,index.html是路径,也就是用户要访问的超文本文件所处在目标主机的位置。域名是ip地址具有唯一性,路径是目标主机上特定路径的一个文件,这两个就表示了全网内唯一的文件。

但是我们发现好多URL没有端口号啊

http://www.example.com/index.html

实际上就是访问:http://www.example.com/index.html,因为有些协议有默认的端口号如http默认端口号是80.https默认端口号是443。

urlencode 和 urldecode

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

HTTP协议请求与响应格式

HTTP请求
在这里插入图片描述
在这里插入图片描述
首行:协议+url+协议版本
请求报头(Header):请求的属性,冒号分割的键值对;每组属性之间用\r\n分割,遇到空行表示Header部分结束了,如上图。
请求正文(Body):空行后面的内容都是Body,Body允许为空字符串,如果Body存在,则在Header 中会有一个 Content-Length 属性来标识 Body 的长度。

换行符和空行等特殊字符,是http能够做到报头和有效载荷的分离。http协议,序列化和反序列化用的是特殊字符进行子串拼接,不依赖第三方库


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

HTTP常见方法

GET方法
你访问网址的时候,其实就是发了个 GET 请求。
用于请求URL指定的资源。

POST方法
用于传输实体的主体, 通常用于提交表单数据。
就是提交数据,参数放在请求正文中。

PUT 方法
用于传输文件, 将请求报文主体中的文件保存到请求 URL 指定的位置。

HEAD 方法
与 GET 方法类似, 但不返回报文主体部分, 仅返回响应头。

DELETE 方法
用于删除文件, 是 PUT 的相反方法

OPTIONS 方法
用于查询针对请求 URL 指定的资源支持的方法。

Http状态码

在这里插入图片描述
最常见的状态码, 比如 200(OK), 404(Not Found), 403(Forbidden), 302(Redirect, 重定向), 504(Bad Gateway)。
在这里插入图片描述

关于重定向

HTTP 状态码 301(永久重定向) 和 302(临时重定向) 都依赖 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 地址。

临时重定向不是永久的,用在比如登录调整语言切换。永久重定向是永久的,用在域名变更。理解一下这两个用途的不同,就可以窥见两个重定向的不同了。

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 连接。

二、构建http服务器

基于TCP通信,服务器的传输层是用之前写的模版方法进行构造。

Util.hpp 工具类

#pragma once
#include <iostream>
#include <string>
#include <fstream>using namespace std;class Util
{
public:static bool Getoneline(string& in,string* out,const string& sep) //获取报文请求行{auto pos=in.find(sep);if(pos==string::npos){return false;}*out+=in.substr(0,pos);in.erase(0, pos + sep.size());return true;}static bool ReadFileConet(string &filename, string *out) // 把html文件写入应答正文{// version2 : 以二进制方式进行读取int filesize = FileSize(filename);if (filesize > 0){std::ifstream in(filename);if (!in.is_open())return false;out->resize(filesize);in.read((char *)(out->c_str()), filesize);in.close();}else{return false;}return true;}static int FileSize(const std::string &filename)  //获取正文长度{std::ifstream in(filename, std::ios::binary);if (!in.is_open())return -1;in.seekg(0, in.end);int filesize = in.tellg();in.seekg(0, in.beg);in.close();return filesize;}
};

Getoneline,是解析一行字符串,比如提取请求行。
ReadFileConet,读取HTML、图片等资源文件。
FileSize,获取文件大小,设置Content-Length。

下面具体用到再具体问题具体分析。

Http.hpp应用层Http协议的代码

#pragma once#include "Tcpserver.hpp"
#include "Util.hpp"
#include "Log.hpp"
#include <unordered_map>
#include <sstream>using namespace std;
using namespace SocketModule;
using namespace LogModule;const std::string gspace = " ";
const std::string glinespace = "\r\n";
const std::string glinesep = ": ";const string wwwroot = "./wwwroot";
const string homepage = "index.html";
const string page_404 = "/404.html";class HttpRequest   //处理客户端http请求
{
public:HttpRequest(): _is_interact(false){}string Serialize(){return string();}void ParseReqLin(string &req_line){stringstream ss(req_line);ss >> _method >> _uri >> _version;}bool Deserialize(string &in)    //反序列化{string req_line;bool r = Util::Getoneline(in, &req_line, glinespace);   //获取请求行ParseReqLin(req_line);if (_uri == "/"){_uri = wwwroot + _uri + homepage;}else{_uri = wwwroot + _uri;}//  _uri: ./wwwroot/login?username=zhangsan&password=123456LOG(LogLevel::DEBUG) << "method -> " << _method;LOG(LogLevel::DEBUG) << "uri -> " << _uri;LOG(LogLevel::DEBUG) << "version -> " << _version;const string temp = "?";    //url请求前面有?,代表浏览器要查询auto pos = _uri.find(temp);if (pos == string::npos){return true;}_args = _uri.substr(pos + temp.size());_uri = _uri.substr(0, pos);_is_interact = true;return true;}string Uri() { return _uri; }bool isInteract() { return _is_interact; }std::string Args() { return _args; }~HttpRequest(){}public:string _method;                         //请求方法string _uri;                            //URIstring _version;                        //http 版本unordered_map<string, string> _headers; //请求报头string _blankline;                      //空行string _text;                           //正文std::string _args; // 请求bool _is_interact; // 动态还是静态
};class HttpRespose   //服务端做应答
{
public:HttpRespose(): _blankline(glinespace){}string Serialize()  //序列化{string status_line = _version + gspace + to_string(_code) + gspace + _desc + glinespace;string kv_line;for (auto &head : _headers){string head_line = head.first + glinesep + gspace + head.second + glinespace;kv_line += head_line;}string resstr = status_line + kv_line + _blankline + _text;return resstr;}bool Deserialize(string &in){return true;}void SetTargetFile(const string &target){_targetfile = target;}void SetCode(int code){_code = code;switch (_code){case 200:_desc = "OK";break;case 404:_desc = "Not Found";break;case 302:_desc = "See Other";break;default:break;}}void SetHeader(const string &key, const string &value){auto iter = _headers.find(key);if (iter != _headers.end()){return;}_headers.insert(make_pair(key, value));}string Uri2Suffix(const string &targetfile){auto pos = targetfile.rfind('.');if (pos == string::npos){return "text/html";}string suffix = targetfile.substr(pos);if (suffix == ".html" || suffix == ".htm")return "text/html";else if (suffix == ".jpg")return "image/jpeg";else if (suffix == ".png")return "image/png";elsereturn "";}bool MakeResponse(){_version = "HTTP/1.1";if (_targetfile == "./wwwroot/redir_test") // 测试重定向{SetCode(302);SetHeader("Location", "https://www.qq.com/");return true;}int filesize;string suffix;bool re = Util::ReadFileConet(_targetfile, &_text);//读取目标文件的内容if (!re){// SetCode(302);// SetHeader("Location","http://120.46.84.37:8080/404.html"); //通过重定向方式访问404页面SetCode(404);_targetfile = wwwroot + page_404;Util::ReadFileConet(_targetfile, &_text);filesize = Util::FileSize(_targetfile); // 拿到正文大小suffix = Uri2Suffix(_targetfile);SetHeader("Content-Length", to_string(filesize));SetHeader("Content-Type", suffix);}else{SetCode(200);filesize = Util::FileSize(_targetfile); // 拿到正文大小suffix = Uri2Suffix(_targetfile);SetHeader("Content-Length", to_string(filesize));SetHeader("Content-Type", suffix);}return true;}void SetText(const std::string &t){_text = t;}~HttpRespose(){}public:string _version;int _code;string _desc;unordered_map<string, string> _headers;string _blankline;string _text;string _targetfile;
};using http_func_t = function<void(HttpRequest &req, HttpRespose &resp)>;
class Http
{
public:Http(uint16_t port): _tserver(make_unique<Tcpserver>(port)){}void HttpRequestserver(shared_ptr<Socket> &sockfd, InetAddr &client){string reqstr;int n = sockfd->Recv(&reqstr);if (n > 0){cout << "#############################" << endl;cout << reqstr << endl;cout << "#############################" << endl;HttpRequest req;  // 请求对象HttpRespose resq; // 应答对象req.Deserialize(reqstr);if (req.isInteract()) // 动态{if (_route.find(req.Uri()) == _route.end()){}else{_route[req.Uri()](req, resq);string response_str = resq.Serialize();sockfd->Send(response_str);}}else // 静态{resq.SetTargetFile(req.Uri());if (resq.MakeResponse()) // 封装报文{string resq_str = resq.Serialize();sockfd->Send(resq_str);}}}}void Start(){_tserver->Start([this](shared_ptr<Socket> &sockfd, InetAddr &client){ this->HttpRequestserver(sockfd, client); });}void RegisterService(const string name, http_func_t h){string key = wwwroot + name;auto iter = _route.find(key);if (iter == _route.end()){_route.insert(make_pair(key, h));}}~Http(){}private:unique_ptr<Tcpserver> _tserver;unordered_map<string, http_func_t> _route;
};

HttpRequest 处理客户端http请求

class HttpRequest   //处理客户端http请求
{
public:HttpRequest(): _is_interact(false){}string Serialize(){return string();}void ParseReqLin(string &req_line){stringstream ss(req_line);ss >> _method >> _uri >> _version;}bool Deserialize(string &in)    //反序列化{string req_line;bool r = Util::Getoneline(in, &req_line, glinespace);   //获取请求行ParseReqLin(req_line);if (_uri == "/"){_uri = wwwroot + _uri + homepage;}else{_uri = wwwroot + _uri;}//  _uri: ./wwwroot/login?username=zhangsan&password=123456LOG(LogLevel::DEBUG) << "method -> " << _method;LOG(LogLevel::DEBUG) << "uri -> " << _uri;LOG(LogLevel::DEBUG) << "version -> " << _version;const string temp = "?";    //url请求前面有?,代表浏览器要查询auto pos = _uri.find(temp);if (pos == string::npos){return true;}_args = _uri.substr(pos + temp.size());_uri = _uri.substr(0, pos);_is_interact = true;return true;}string Uri() { return _uri; }bool isInteract() { return _is_interact; }std::string Args() { return _args; }~HttpRequest(){}public:string _method;                         //请求方法string _uri;                            //URIstring _version;                        //http 版本unordered_map<string, string> _headers; //请求报头string _blankline;                      //空行string _text;                           //正文std::string _args; // 请求bool _is_interact; // 动态还是静态
};

私有成员变量中,根据http协议请求,请求行有请求方法、URL、http版本,然后是请求报头,空行正文,根据这些定义这些成员变量。请求字符串这个成员变量是从url中提取出来的用户的需求。
成员变量中还有一个判断是是否是动态还是静态的,所谓静态资源就是内容是固定的,不需要数据库的,动态资源就是根据用户的请求内容变化,通常需要数据库,如登录、注册等。

服务器要想拿到客户端的http请求,要对http通过网络发来的http报文做出解析,注意http报文从客户端发到主机不用序列化,服务器的应答发到客户端不用反序列化(HTTP 请求和响应虽然是“文本格式”,但本质上已经是一种“通用序列化格式”了。HTTP 的本质,就是一种“轻量文本格式”的协议,天然具备自解释性,这就是它“看起来不用手动序列化/反序列化”的根本原因)。
其实我代码中处理客户端http请求的反序列化,更像是解析。

解析请求过程,通过工具类中的Getoneline是解析一行字符串,来提取请求行。把请求行分割为三部分,请求方法、URL、版本。(在这里说一下,请求的资源,比如网页内容必须是服务器特定路径下的文件,放在web根目录中)如果URL是根目录(web根目录)就把路径拼接为网页的首页,如果URL用户申请的特定网页就拼接上用户请求的url。有时候urll后面带一个?,后面就是用户提交的查询参数了,做一下判断。截取一下 _uri 是用户请求的页面,比如 /login.html_args 是查询参数,比如 username=han&password=123。有查询参数是动态页面。

上面的GET方法通常在获取静态资源使用,POST方法通常在获取动态资源使用。

客户端发来的请求,服务器第一时间是拆第一行,搞清楚是 GET 还是 POST,要访问哪个资源(URL),是静态页面还是带查询的动态页面,然后拼成服务器本地路径,再判断是否有参数 —— 这就完成了解析的第一步。


HttpRespose.hpp服务端做应答

class HttpRespose   //服务端做应答
{
public:HttpRespose(): _blankline(glinespace){}string Serialize()  //序列化{string status_line = _version + gspace + to_string(_code) + gspace + _desc + glinespace;string kv_line;for (auto &head : _headers){string head_line = head.first + glinesep + gspace + head.second + glinespace;kv_line += head_line;}string resstr = status_line + kv_line + _blankline + _text;return resstr;}bool Deserialize(string &in){return true;}void SetTargetFile(const string &target){_targetfile = target;}void SetCode(int code){_code = code;switch (_code){case 200:_desc = "OK";break;case 404:_desc = "Not Found";break;case 302:_desc = "See Other";break;default:break;}}void SetHeader(const string &key, const string &value){auto iter = _headers.find(key);if (iter != _headers.end()){return;}_headers.insert(make_pair(key, value));}string Uri2Suffix(const string &targetfile){auto pos = targetfile.rfind('.');if (pos == string::npos){return "text/html";}string suffix = targetfile.substr(pos);if (suffix == ".html" || suffix == ".htm")return "text/html";else if (suffix == ".jpg")return "image/jpeg";else if (suffix == ".png")return "image/png";elsereturn "";}bool MakeResponse(){_version = "HTTP/1.1";if (_targetfile == "./wwwroot/redir_test") // 测试重定向{SetCode(302);SetHeader("Location", "https://www.qq.com/");return true;}int filesize;string suffix;bool re = Util::ReadFileConet(_targetfile, &_text);//读取目标文件的内容if (!re){// SetCode(302);// SetHeader("Location","http://120.46.84.37:8080/404.html"); //通过重定向方式访问404页面SetCode(404);_targetfile = wwwroot + page_404;Util::ReadFileConet(_targetfile, &_text);filesize = Util::FileSize(_targetfile); // 拿到正文大小suffix = Uri2Suffix(_targetfile);SetHeader("Content-Length", to_string(filesize));SetHeader("Content-Type", suffix);}else{SetCode(200);filesize = Util::FileSize(_targetfile); // 拿到正文大小suffix = Uri2Suffix(_targetfile);SetHeader("Content-Length", to_string(filesize));SetHeader("Content-Type", suffix);}return true;}void SetText(const std::string &t){_text = t;}~HttpRespose(){}public:string _version;int _code;string _desc;unordered_map<string, string> _headers;string _blankline;string _text;string _targetfile;
};

http协议应答中,状态行包含hettp版本、状态码、找状态码描述。私有成员变量要设置相应的变量。响应报头、空行、响应正文和上面处理请求的私有成员变量一样。

先说序列化Serialize(),不是真正的序列化,是搞成Http协议标准应答报文的格式。第一层状态行,给_version,_code,_desc拼接起来。封装响应报头,用范围for把多个响应报头拿到对其按照标准报文格式进行拼接。最后在拼接上空行和响应正文。

设置状态码和描述SetCode,根据规定的状态码对应的方案,写了几个重要的状态码以及描述。

添加响应报头信息,就是在响应报头中贴标签,比如这个服务器叫啥。就是告诉浏览器一些关系的信息,比如返回的是HTML还是图片,SetHeader函数把这些信息一条条加进去。

Uri2Suffix函数根据请求的URL去判断是什么文件,然后根据它的MIME类型,比如“image/jpeg”这个结果会被放到响应报头的的Content-Type字段里。响应报头里的 Content-Type 字段标明了服务器返回的内容类型(MIME 类型),这就像给浏览器打个招呼:“哥们,我这是一张图,你别当成网页来渲染哈”。

MakeResponse主逻辑根据目标文件生成应答。设置版本,加个重定向主要是为了测试一下重定向,如果路径是xxx,就跳转到设定的目标网页,这里是临时重定向。现在我们要做的是把目标 HTML 文件的内容读出来,作为响应报文的正文(body)发回去给浏览器,通过工具类ReadFileConet把目标HTML文件写入响应正文。如果读取错误,返回404错误页面,这个错误页面的html也是在web根目录中,目标路径_targetfile重新设置为404页面重新写入正文。为了设置响应报头信息和MIME类型用上面的SetHeaderUri2Suffix进行设置。读取成功目标HTML文件成功写入响应正文,为了设置响应报头信息和MIME类型用上面的SetHeaderUri2Suffix进行设置。


Http类最核心调度逻辑

using http_func_t = function<void(HttpRequest &req, HttpRespose &resp)>;
class Http
{
public:Http(uint16_t port): _tserver(make_unique<Tcpserver>(port)){}void HttpRequestserver(shared_ptr<Socket> &sockfd, InetAddr &client){string reqstr;int n = sockfd->Recv(&reqstr);if (n > 0){cout << "#############################" << endl;cout << reqstr << endl;cout << "#############################" << endl;HttpRequest req;  // 请求对象HttpRespose resq; // 应答对象req.Deserialize(reqstr);if (req.isInteract()) // 动态{if (_route.find(req.Uri()) == _route.end()){}else{_route[req.Uri()](req, resq);string response_str = resq.Serialize();sockfd->Send(response_str);}}else // 静态{resq.SetTargetFile(req.Uri());if (resq.MakeResponse()) // 封装报文{string resq_str = resq.Serialize();sockfd->Send(resq_str);}}}}void Start(){_tserver->Start([this](shared_ptr<Socket> &sockfd, InetAddr &client){ this->HttpRequestserver(sockfd, client); });}void RegisterService(const string name, http_func_t h){string key = wwwroot + name;auto iter = _route.find(key);if (iter == _route.end()){_route.insert(make_pair(key, h));}}~Http(){}private:unique_ptr<Tcpserver> _tserver;unordered_map<string, http_func_t> _route;
};

私有成员变量_tserver是Tcpserver对象,负责TCP监听和连接。_route是unordered_map对象,用于注册动态接口。

void Start(){_tserver->Start([this](shared_ptr<Socket> &sockfd, InetAddr &client){ this->HttpRequestserver(sockfd, client); });}

启动Tcpserver,用lambda回调函数,调用HttpRequestserver收客户端的请求。

RegisterService(),注册服务路径->对应处理函数。

HttpRequestserver调用Recv接收请求信息,构造HttpRequest请求对象和 HttpRespose应答对象。对请求进行解析提取客户端用户要访问的目标html,根据是否是动态请求分发,动态查找_route,调用注册回调,静态构造目标文件路径,调用MakeResponse()生成响应报文。将序列化后的响应报文Send给客户端。


main.cc


#include "Http.hpp"
void Login(HttpRequest& req, HttpRespose& resp)
{LOG(LogLevel::DEBUG) << req.Args() << ", 成功进入到了处理数据的逻辑";std::string text = "hello: " + req.Args();// 登录认证resp.SetCode(200);resp.SetHeader("Content-Type","text/plain");resp.SetHeader("Content-Length", std::to_string(text.size()));resp.SetText(text);LOG(LogLevel::DEBUG) <<"返回了啊";
}
// http port
int main(int argc, char *argv[])
{uint16_t port = std::stoi(argv[1]);Enable_Console_Log_Strategy(); // 启用控制台输出std::unique_ptr<Http> httpsvr = std::make_unique<Http>(port);httpsvr->RegisterService("/login", Login); // httpsvr->Start();return 0;
}

浏览器访问 /login 这个路径时,我们希望服务器:不是去找静态文件(因为根本没有有/login.html),而是执行一个 函数,比如去数据库查用户是否存在,返回一段 JSON 或重定向。Http类中成员变量 unordered_map<string, http_func_t> _route;key是路由路径,value是处理这个路径的处理函数。处理函数是

void Login(HttpRequest& req, HttpRespose& resp)
{LOG(LogLevel::DEBUG) << req.Args() << ", 成功进入到了处理数据的逻辑";std::string text = "hello: " + req.Args();// 登录认证resp.SetCode(200);resp.SetHeader("Content-Type","text/plain");resp.SetHeader("Content-Length", std::to_string(text.size()));resp.SetText(text);LOG(LogLevel::DEBUG) <<"返回了啊";
}

注册函数,我们告诉 Http:以后只要有人请求这个路径 /login,你就调用 handler 这个函数去处理。

void RegisterService(const string name, http_func_t h){string key = wwwroot + name;auto iter = _route.find(key);if (iter == _route.end()){_route.insert(make_pair(key, h));}}

main.cc中使用方式,httpsvr->RegisterService(“/login”, Login);

在这里插入图片描述


测试结果
在这里插入图片描述
在这里插入图片描述
在这里插入图片描述
Http服务器:源码

三、Cookie和Session

HTTP 协议是一个无连接、 无状态的协议, 即每次请求都需要建立新的连接, 且服务器不会保存客户端的状态信息。
无连接是每次请求响应之后,客户端与服务器就断开了。
无状态是服务器不会记住上一次请求的任何信息。

在这里插入图片描述

cookie是浏览器保存的一小段文本信息,随请求发给客户端,这种机制是为了应对上述情况的一种浏览器实现的机制,cookie会存储用户的账号和密码等信息。如果没有cookie那么每次访问网站都要重新登录。cookie是在客户端浏览器实现的。你打开淘宝登录了一次,下次刷新网页还能记住你是谁,就是因为 cookie 帮你“记住”了登录信息。

session是服务器上的一块数据空间,用来存储用户信息的。

这两者进行数据交换,为防止信息泄露,要用其他机制对信息进行加密,防止在网络传输中被泄露。


http://www.hkcw.cn/article/dlpDuYfhNk.shtml

相关文章

[ctfshow web入门] web80

信息收集 过滤了php和data if(isset($_GET[file])){$file $_GET[file];$file str_replace("php", "???", $file);$file str_replace("data", "???", $file);include($file); }else{highlight_file(__FILE__); }解题 大小写…

移动安全Android——客户端数据安全

本地文件权限配置 测试流程 &#xff08;1&#xff09;手机运行待测APP应用&#xff0c;adb执行命令找到APP包名 adb shell dumpsys activity top|findstr ACTIVITY &#xff08;2&#xff09;adb shell 进入设备&#xff0c;以Root权限进入/data/data/package包名目录下 c…

AI生态警报:MCP协议风险与应对指南(下)——MCP Host安全

AI生态警报&#xff1a;MCP协议风险与应对指南&#xff08;上&#xff09;——架构与供应链风险https://blog.csdn.net/WangsuSecurity/article/details/148335401?sharetypeblogdetail&sharerId148335401&sharereferPC&sharesourceWangsuSecurity&spm1011.24…

机房网络设备操作安全管理制度

该制度围绕机房网络设备操作安全,规定账号实行系统管理员、操作管理员、一般用户三级分级管理,遵循最小授权和权限分割原则,账号需实名制、禁止共享及转借,密码设置需至少 8 位、3 种字符组合且每 3 个月修改一次;高危指令执行需上级审批、双人核查,远程登录需限制权限、…

Root权限:解锁Android的终极力量

Root后的功能扩展 Root后可以实现的高级功能&#xff0c;如系统级备份、自定义ROM、性能优化、广告屏蔽等。 Root的风险与防范 讨论Root可能导致的安全问题&#xff0c;如恶意软件攻击、系统不稳定、保修失效等&#xff0c;提出降低风险的建议&#xff0c;如使用可信工具、备…

亚马逊数据采集软件完全指南:从工具原理到实战落地

亚马逊数据采集软件有哪些&#xff1f;在数字化商业浪潮中&#xff0c;亚马逊作为全球电商巨头&#xff0c;其平台上蕴含着海量的数据宝藏。对于卖家、品牌商以及市场分析师而言&#xff0c;精准获取和分析这些数据&#xff0c;成为了在激烈竞争中脱颖而出的关键。从产品定价的…

免费高清多功能录屏软件推荐

软件介绍 今天为大家介绍一款功能全面的免费录屏软件 - 云豹录屏大师。 录屏格式支持 这款软件特别强大&#xff0c;能够录制多种常见视频格式&#xff0c;包括MP4、AVI、WMV等格式&#xff0c;满足不同场景的录制需求。 高帧率支持 软件最高支持120帧的录制效果&#xff0…

【交通 Traffic Transformer】同一篇文章,内容排版稍有不同 | 交通预测模型中,Transformer相比传统GCN模型有何优势?

冰冻三尺,非一日之寒。 前情提要: 【Traffic Transformer】将 Transformer 应用于 交通预测领域中 | 动态和分层交通时空特征 | 时空模型比纯时间模型的性能要好得多 | 定义不好的相邻矩阵会损害模型Transformer相比传统GCN模型在交通预测中具有三大核心优势: 1、动态空间依…

docker-compose搭建prometheus以及grafana

1. 什么是 Prometheus&#xff1f; Prometheus 是一个开源的系统监控和告警工具&#xff0c;由 SoundCloud 于 2012 年开始开发&#xff0c;现为 CNCF&#xff08;Cloud Native Computing Foundation&#xff09;项目之一。它特别适合云原生环境和容器编排系统&#xff08;如 …

AI科技前沿动态:5.26 - 5.30 一周速览

目录 ⭐ 本周热点&#x1f4a1; 阿里巴巴开源自主搜索 AI 智能体 WebAgent&#x1f4a1; 我国首个软件开发 AI 智能体标准发布&#xff0c;20 余家巨头联手参编&#x1f4a1; 刚刚&#xff0c;新版DeepSeek-R1正式开源&#xff01;直逼o3编程强到离谱&#xff0c;一手实测来了 …

【google 论文】Titans: Learning to Memorize at Test Time

核心思想与贡献&#xff1a; 这篇论文的核心贡献在于提出了一种新的神经网络长期记忆模块 (neural long-term memory module)&#xff0c;并基于此构建了一个名为 Titans 的新型系列架构。这个架构旨在克服现有模型&#xff08;如Transformers&#xff09;在处理超长序列和长期…

VASP 教程:VASP 结合 Phonopy 计算硅的比热容

VASP 全称为 Vienna Ab initio Simulation Package&#xff08;The VASP Manual - VASP Wiki&#xff09;是一个计算机程序&#xff0c;用于从第一性原理进行原子尺度材料建模&#xff0c;例如电子结构计算和量子力学分子动力学。 Phonopy&#xff08;Welcome to phonopy — Ph…

企业数字化转型的7个难点

数字化转型不是一个有始有终的项目&#xff0c;而是一个持续变革的过程&#xff0c;过程漫长&#xff0c;且险象环生。需要领导者带领企业从成功或不成功的经验里持续反思、持续学习。 近年来&#xff0c;以移动互联网、云计算、大数据、人工智能等为代表的新一代数字化技术正在…

华为OD机试真题——简易内存池(2025A卷:200分)Java/python/JavaScript/C++/C/GO最佳实现

2025 A卷 200分 题型 本文涵盖详细的问题分析、解题思路、代码实现、代码详解、测试用例以及综合分析; 并提供Java、python、JavaScript、C++、C语言、GO六种语言的最佳实现方式! 本文收录于专栏:《2025华为OD真题目录+全流程解析/备考攻略/经验分享》 华为OD机试真题《简易…

mysql-mysql源码本地调试

前言 先进行mysql源码本地编译&#xff1a;mysql源码本地编译 1.本地调试 这里以macbook为例 1.使用vscode打开mysql源码 2.创建basedir目录、数据目录、配置文件目录、配置文件 cd /Users/test/ mkdir mysqldir //创建数据目录和配置目录 cd mysqldir mkdir conf data …

华为OD机试真题——查找接口成功率最优时间段(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…

硬件I2C和软件I2C的区别

硬件I2C和软件I2C的区别 一、硬件I2C 1、硬件IC的局限性及学习意义 尽管硬件IC外设在STM32等微控制器中提供了标准化的通信支持&#xff0c;但在实际应用中&#xff0c;其稳定性可能存在问题。例如&#xff0c;某些情况下外设会因事件检测异常而进入死锁状态&#xff0c;仅能…

PyCharm接入DeepSeek,实现高效AI编程

介绍本土AI工具DeepSeek如何结合PyCharm同样实现该功能。 一 DeepSeek API申请 首先进入DeepSeek官网&#xff1a;DeepSeek 官网 接着点击右上角的 “API 开放平台“ 然后点击API keys 创建好的API key&#xff0c;记得复制保存好 二 pycharm 接入deepseek 首先打开PyCh…

大模型-attention汇总解析之-MQA

MQA&#xff0c;即 “Multi-Query Attention”&#xff0c;是减少 KV Cache 的一次的一种大胆尝试&#xff0c;首次提出自《Fast Transformer Decoding: One Write-Head is All You Need》&#xff0c; 在2019 年减少 KV Cache 就已经是研究人员非常关注的一个课题了。MQA 的思…

华为OD机试真题——游戏分组王者荣耀(2025A卷:100分)Java/python/JavaScript/C/C++/GO最佳实现

2025 A卷 100分 题型 本专栏内全部题目均提供Java、python、JavaScript、C、C++、GO六种语言的最佳实现方式; 并且每种语言均涵盖详细的问题分析、解题思路、代码实现、代码详解、3个测试用例以及综合分析; 本文收录于专栏:《2025华为OD真题目录+全流程解析+备考攻略+经验分…