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

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

🔥个人主页🔥:孤寂大仙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是服务器上的一块数据空间,用来存储用户信息的。

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

相关文章:

  • Flutter 嵌套H5 传参数
  • 芯片:数字时代的算力引擎——鲲鹏、升腾、海光、Intel 全景解析
  • 快捷键IDEA
  • [网页五子棋][匹配模式]创建房间类、房间管理器、验证匹配功能,匹配模式小结
  • Python打卡训练营Day40
  • 《 PyTorch 2.3革新:torch.compile自动生成CUDA优化内核全解》
  • 使用 SymPy 操作三维向量的反对称矩阵
  • 树莓派安装openwrt搭建软路由(ImmortalWrt固件方案)
  • 历年厦门大学计算机保研上机真题
  • Prevent this information from being displayed to the user 修复方案
  • day14 leetcode-hot100-26(链表5)
  • vscode实时预览编辑markdown
  • Java Spring Boot 自定义注解详解与实践
  • Camera相机人脸识别系列专题分析之六:MTK ISP6S平台人脸识别fdnode流程FdNodeImp.cpp详解
  • 历年四川大学计算机保研上机真题
  • rm删除到回收站
  • RustDesk 搭建自建服务器并设置服务自启动
  • deepseek问答记录:请讲解一下torch.full_like()
  • 大数据量下的数据修复与回写Spark on Hive 的大数据量主键冲突排查:COUNT(DISTINCT) 的陷阱
  • 基本数据指针的解读-C++
  • 部队网站建设/品牌推广软文200字
  • 微信如何做商城网站/seo排名优化怎么样
  • 做企业网站的研究现状/seo如何优化关键词上首页
  • 浙江鼎兴建设有限公司网站/完整的品牌推广方案
  • 网站数据库修改密码要怎么做/十大骗子教育培训机构
  • ps 做ui比较好的网站有哪些/产品推广运营方案