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

Linux:探究HTTP协议

Linux:探究HTTP协议

认识Http:

HTTP 协议全称是超文本传输协议(HyperText Transfer Protocol),是互联网中客户端(如浏览器)与服务器之间传输数据的核心规则。

HTTP 是一种应用层协议,主要作用是规范客户端和服务器之间的 “沟通方式”。

这里的 “超文本” 不仅指文字,还包括图片、视频、链接等多种网络资源。

它本质是一套约定:客户端该如何发请求、服务器该如何回响应,数据该用什么格式封装

简单来说Http就是用于我们web上网获取数据的协议。

上图是一个URL的超链接,可能看到这里会有疑惑,我们在Sock基础了解到,网络通信的基本是IP加端口号,可这里的超链接并没有看到ip与端口。

解析URL:

https:

表示网络协议,成熟的网络协议都与端口号进行挂钩,所以这就能解释为什么我们之前在Sock通信时候,进行绑定端口号时,不能绑定0~1023的端口,只能从1204开始绑定。因此成熟的网络协议都是与端口号进行挂钩的,https绑定443协议,http绑定80.

域名:

域名是用于做IP地址转换的,为什么不直接用IP进行登陆访问。主要是因为不方便。你知道www.baidu.com是百度的域名,但你不知道百度的IP地址,IP地址不好记。所以web内部自动帮我们将域名解析成IP地址了。

现在IP有了端口号也有了,不就能访问网络上唯一一台主机的唯一一个进程了吗,所以在域名后面本质访问的就是,bilibili服务器下video目录下的文件。

所以HPPT的本质也是Socket通信,人们上网究其本源只有两种目的,一是从网络上获取资源,二是上传资源到网络。而这里的资源可以是网页,图片,音视频等,但这些本质还是文件。当我们没获取这些文件的时候,这些资源就存在与Linux服务器的内部

认识Http的请求结构:

我们能意识到,所谓的协议就是一个约定俗成的结构体,因此Http协议也是一个结构体。

请求行解析:

请求方法:

请求方法有多种,但最为常用的是 GET 与POST。GET用于获取数据,POST用于上传数据。理解角度来说,GET方法通常是用于获取网络资源的,而POST方法通常用于在网站上登陆账号密码。GET方法也可以用于登陆信息,但GET会把登陆信息添加在URL的后。而POST方法则是像上图一样,将登陆信息保存在正文中,而正文的内容通常是不会直接进行显示的,所以具有一定的私密性。

空格:

用于拆分请求行数据

URI:

表面你要请求的资源路径

Http版本:

Http分为两个版本,1.0与1.1。那为什么请求会添加上版本呢?其实也很好理解,就像微信qq等,它们的服务器更新了版本,但你的手机不一定更新。所以微信qq检测到你不是最新版本,有些方法不会给你进行提供,而使用老版本进行交互。

请求报头:

请求报头里存放着多个请求属性,这里我们重点挑几个来说明。

Content-Length: 30

这里Content-Length: 30就表示正文内容的长度是30个字符,那为什么要有Content-Length这个属性呢?在这里首先我们要先知道TCP传输协议是面向字节流的传输协议。我们数据传输是逐级向下的,而HTTP是应用层协议,所以我们的数据是先要交给操作系统内核的缓冲区,至于操作系统什么时候发送数据,一次性发多少数据,由操作系统决定。

那么这样就会导致,数据可能发送不完全,可能多发也可能少发。那为了确保报文的完整性,就需要明确正文内容的长度。而如何区分正文与请求报头也很简单,HttpRequest是以空行作为分隔符来区分数据报与有效载荷。

所以我们在读取HttpRequest时,就可以以空行作为判断依据,当一次读取并没有读到空行,就表示该HttpRequest并不完整,需要继续读取。而Content-Length则可以确定正文内容的大小。如果此时我们读取到空行则表示读取到一个完整的报头,那么报头大小+空行+正文内容=一个完整的报文大小。因此我们就可以获取到一个完整的报文大小,如果说此时读取上来的内容还不足以一个完整报文,则继续读取,直到读取到一个完整的报文大小。

Content-Type:

Content-Type表示当前内容的属性。

我们的HTTP页面不止有我们熟知的.html文件格式,图片.jpg,视频.mp4等格式,那么Http为了区分这些资源格式,就有一个Content-Type表

那么这种不同的格式资源就是为了让web浏览器进行与之相对应的渲染,不然无法呈现效果。

认识Http的应答结构:

Http的应答几乎与Hppt的请求类似,所以小编只挑选几个与HTTP请求不同的属性

状态码与状态码描述:

Http的状态码及描述:

在我们上网的时候,有时候想访问一些网页,但点击去后会显示404,也就是页面不存在了。而404就是我们熟知的Http状态码。

1开头的状态码,表示服务器收到客户端发来的请求,并且需要继续执行剩下的操作。

2开头的状态码表示,请求接受成功并处理。

3开头的状态码表示网站进行重定向处理。

4开头表示客户端错误,主要原因可能是访问的目标资源不存在

5开头表示服务器错误。

重定向:

重定向本质是一个页面跳转到另一个页面。而3开头的状态码有两种重定向方式301表示永久重定向,302表示临时重定向,而这两个有什么区别呢?

就比如现在在你家小区的东门有一家麻辣烫店你很喜欢去吃。但有一天你去店门口的时候发现店门口有一家告示,告示上写着:由于小区东门修路,本店暂时搬到小区西门。所以这次你去这家麻辣烫店就去西门吃饭。

而过来一两个星期你又想吃麻辣烫了,但你这时候并不知道麻辣烫店是否有搬回来,于是你还是会去门的老店光顾,那么这就是临时重定向。

而如果麻辣烫店老板发现在西门的生意比东门好做,此时就重新给东门贴了一份告示,说由于西门生意较好,本店永久搬至西门。因此当你下次再去吃麻辣烫的时候,你就不会去东门,只会去西门。这就是永久重定向。

临时重定向通常用于类似当你没有登陆b站账户时候却想看视频,本来请求的是视频页面,但服务器检测到你没有登陆,就会对请求的网页进行重定向到登陆页面。

而永久重定向就类似于网站永久搬迁至另一个域名,当你客户机请求老的网站时候,如果有人维护,就会给你重定向到新搬迁的网站上,当你下一次访问时候就是访问新的网站。

Location属性:

当服务器需要引导客户端访问新地址时,会在 3xx 系列状态码(如 301 永久重定向、302 临时重定向)的响应头中携带 Location,告知客户端新的资源 URL。

额外补充:

长连接connection:

什么是长短连接:

简单来说短连接就是,你请求一次数据,我就返回一次数据,而长连接就是你一次性请求多个数据,我全部处理完在给你返回去。

还是以B站首页为例,当我们登陆B站首页时候,除了基本的HTML文本,还有图片视频等。而每一个图片和视频都算一次请求。如果是短服务的话,我们的Web客户端就要进行多次请求,web客户端先请求B站首页HTML,对方返回HTML首页后结束。接着再次发起请求,请求首页的其中一个图片,同样的B站再给客户端返回其中一个图片的请求。

所以如果是短服务的话,一个页面就有非常多个请求需要处理,十分费事,而且每次处理请求与应答前,客户机与服务器都得先建立连接。

而长服务则类似与创建一个信道后不关闭连接,你有多少就发多少,我都能给你处理,并且我也能处理,因为我拥有解析一个完整报文的能力,能完整解析一个报文就能完整解析多个报文。

在Http的请求报头与回应报头中,如果Connection属性中显示的是Keep-Alive,则表示支持长连接。

Cookie与Session

Http协议的特点是无连接与无状态的。

无连接:

无关连接,HTTP是应用层协议,因此不关系客户机与服务器是否连通,它只负责做两件事情,一是解析传输层传入的报文,处理有效载荷。二是封装报头与有效载荷,形成报文,交给传输层。

无状态:

HTTP 协议的无状态,核心是指服务器不会主动保留客户端过往请求的任何信息,每次请求都是完全独立的 “一次性交互”,所以HTTP不会记录类似于账号密码这种敏感信息。

Cookie:

当我们登陆比如B站等这些需要账号密码才能登陆的网页时,当我们登陆一次后,之后点击其他视频时就不需要再次进行登陆,而过了一段时间后需要再次验证登陆。

但其实我们访问这些需要登陆信息网站的任何页面,其实都需要登陆认证。但如果这个网站有1000个资源,那么我们每次访问这些资源难道都要再一次进行登陆吗,那这种网站谁会去使用。

因此web服务器就会缓存你的账号密码这种数据,也就是Cookie。Cookie会在你第一次登陆网页时候记录你的信息,当你下一次进行登陆的时候会把等登陆验证信息与HTTP协议一同传输,就会自动进行登陆验证,就不需要再次进行验证了。 保存Cookie的方式有两种,一是内存级的,二是文件级的。想要验证这两种方式也很简单,因为浏览器也是进程,如果关闭浏览器后再次打开网页发现需要验证则是内存级,如果不需要则是文件级别的。

但Cookie也有有效期限,也就是当我们一段时间不登陆后就需要重新登陆,也就是Cookie失效。

Session:

在 HTTP 协议中,Session(会话) 是服务器端用来存储客户端会话信息的机制,核心作用是 “记住用户”,以此弥补 HTTP 协议的无状态缺陷。

简单说,Session 就像服务器给每个来访用户发的 “临时身份证”,用户后续访问时出示这张证,服务器就能识别身份并调取之前存储的信息。

Cookie也有致命的缺点,如果客户机被植入了木马病毒,那么病毒可以扫描你的浏览器的cookie文件,就能盗取你所有敏感信息。

因此就产生了Session+Cookie技术。

Session 的核心逻辑是 “敏感数据不存本地,只存服务器”。服务器会为每个用户创建一个唯一的 Session ID(类似临时钥匙),并通过 Cookie 将这个 ID 发送给客户端存储。客户端后续访问时,只需携带 Cookie 中的 Session ID,服务器通过这个 ID 找到对应的 Session(存储在服务器的用户数据,如登录状态、权限等),即可完成身份验证(如自动登录)。

这种机制下,客户端本地的 Cookie 仅存储非敏感的 Session ID,而非敏感信息,即使 Cookie 被盗,攻击者拿到的也只是 “钥匙”,而非 “数据本身”,风险大幅降低。但这只能一定程度上预防风险 —— 若 Session ID 被窃取(如通过 XSS 攻击),攻击者仍可冒充用户,因此还需结合 HTTPS、Cookie 安全属性等措施进一步防护。

模拟实现Http(代码实现):

Common.hpp:

#pragma once#include <iostream>#include <functional>#include <unistd.h>#include <string>#include <cstring>#include <sys/socket.h>#include <sys/types.h>#include <arpa/inet.h>#include <netinet/in.h>enum ExitCode{OK = 0,USAGE_ERR,SOCKET_ERR,BIND_ERR,LISTEN_ERR,CONNECT_ERR,FORK_ERR,OPEN_ERR};class NoCopy{public:NoCopy(){}~NoCopy(){}NoCopy(const NoCopy &) = delete;const NoCopy &operator = (const NoCopy&) = delete;};#define CONV(addr) ((struct sockaddr*)&addr)

Common.hpp主要定义了网络编程中常用的错误码枚举类型以及禁止拷贝的基类NoCopy,通过删除拷贝构造函数与赋值操作符确保派生类不可拷贝。CONV宏用于将各类socket地址结构统一转换为通用的sockaddr指针类型,便于系统调用参数传递。该头文件为后续网络模块封装提供基础支持。

Mutex:

#pragma once#include<iostream>#include<pthread.h>class Mutex{public:Mutex(){pthread_mutex_init(&_lock,nullptr);}void Lock(){pthread_mutex_lock(&_lock);}void UnLock(){pthread_mutex_unlock(&_lock);}~Mutex(){pthread_mutex_destroy(&_lock);}private:pthread_mutex_t _lock;};class MutexGuard{public:MutexGuard(Mutex&mutex):_mutex(mutex){_mutex.Lock();}~MutexGuard(){_mutex.UnLock();}private:Mutex&_mutex;};

Mutex类封装了pthread互斥锁,提供构造即初始化、析构自动销毁的RAII机制。MutexGuard通过栈式管理实现自动加锁与解锁,避免手动调用遗漏导致死锁。两者结合确保多线程环境下临界资源的安全访问,提升代码健壮性与可维护性。

Log.hpp

#pragma once#include <iostream>#include "Mutex.hpp"#include <string>#include <fstream>#include <filesystem>#include <memory>#include <sys/types.h>#include <unistd.h>#include <ctime>#include<sstream>const std::string sep="\r\n";class Strategy{public:virtual void FlushStrategy(std::string &message)=0;};class MonitorStrategy : public Strategy{public:void FlushStrategy(std::string &message)override {MutexGuard mg(_mutex);std::cout << message<<sep;}private:Mutex _mutex;};const std::string logpath = "./log";const std::string logname = "mylog.log";class FileStrategy : public Strategy{public:FileStrategy(std::string path = logpath, std::string file = logname): _path(path), _file(file){if (std::filesystem::exists(_path))return;std::filesystem::create_directories(_path);}void FlushStrategy(std::string &message)override {MutexGuard mg(_mutex);std::string file = _path + (_path.back() == '/' ? "" : "/") + _file;std::ofstream out(file, std::ios::app);if (!out.is_open()){return;}out << message<<sep;}private:Mutex _mutex;std::string _path;std::string _file;};enum LogLevel{DEBUG,INFO,WARNING,ERROR,FATAL};std::string Level_to_String(LogLevel le){switch (le){case DEBUG:return "DEBUG";case INFO:return "INFO";case WARNING:return "WARNING";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "NULL";}}std::string GetTime(){time_t cur_time=time(nullptr);struct tm cur_tm;char timbuff[128];localtime_r(&cur_time,&cur_tm);snprintf(timbuff,sizeof(timbuff),"%4d-%02d-%02d %02d:%02d:%02d",cur_tm.tm_year+1900,cur_tm.tm_mon+1,cur_tm.tm_mday,cur_tm.tm_hour,cur_tm.tm_min,cur_tm.tm_sec);return timbuff;}class Log{public:Log(): _st(std::make_unique<MonitorStrategy>()){}class LogMessage{public:LogMessage(LogLevel le,std::string file,int line,Log &log):_level(le),_src_name(file),_line_number(line),loger(log),_time(GetTime()),_pid(getpid()){std::stringstream ss;ss<<"["<<_time<<" "<<Level_to_String(_level)<<" "<<_pid<<" "<<_src_name<<" "<<_line_number<<"]";log_info=ss.str();}template<class T>LogMessage&operator <<(T value){std::stringstream ss;ss<<value;log_info+=ss.str();return *this;}~LgMessage(){loger._st->FlushStrategy(log_info);}private:std::string _time;LogLevel _level;pid_t _pid;std::string _src_name;int _line_number;std::string log_info;Log &loger;};LogMessage operator()(LogLevel le, std::string file,int line){return LogMessage(le,file,line,*this);}private:std::unique_ptr<Strategy> _st;};Log g_og;#define LOG(level) g_og(level,__FILE__,__LINE__)

Log.hpp主要定义了日志系统的接口与实现框架,通过模板机制支持灵活的日志内容拼接,结合策略模式实现日志输出的可扩展性,确保不同严重级别日志的分类处理。宏定义LOG简化了调用形式,使日志记录简洁直观,提升代码可读性与维护效率。同时,LogMessage的RAII机制保证了每条日志在析构时自动触发刷新策略,确保消息即时写入目标介质。通过封装时间戳、进程ID与源文件信息,系统具备完整的上下文追溯能力,为故障排查提供有力支持。

Sock.hpp

#pragma once#include <iostream>#include "Comd.hpp"#include <memory>#include <sys/types.h>#include <sys/socket.h>#include "Log.hpp"#include "InetAddr.hpp"const int gbacklog = 6;class Socket : public nocopy{public:Socket() {}virtual void SocketOrDie()=0;virtual void BindOrDie(uint16_t port)=0;virtual void ListenOrDie(int backlog)=0;virtual std::shared_ptr<Socket> AcceptOrDie(InetAddr*client)=0;virtual int Recv(std::string *out)=0;virtual void Send(std::string &message)=0;virtual void Close()=0;~Socket() {}public:void BuildTcpServerSocket(uint16_t port,int backlog=gbacklog){SocketOrDie();BindOrDie(port);ListenOrDie(gbacklog);}};class TcpServerSocket : public Socket{public:TcpServerSocket():_sockid(-1){}TcpServerSocket(int sockid):_sockid(sockid){}void SocketOrDie() override{_sockid = socket(AF_INET, SOCK_STREAM, 0);if (_sockid < 0){LOG(LogLevel::FATAL) << "create socket error";exit(Error::SOCK_ERROR);}LOG(LogLevel::DEBUG) << "create socket success";}void BindOrDie(uint16_t port) override{InetAddr server("0.0.0.0", port);int n = bind(_sockid, server.InetAddrPtr(), server.Lenth());if (n < 0){LOG(LogLevel::FATAL) << "bind error";exit(Error::BIND_ERROR);}LOG(LogLevel::DEBUG) << "bind success";}void ListenOrDie(int backlog) override{int n = listen(_sockid, backlog);if (n < 0){LOG(LogLevel::FATAL) << "listen error";exit(Error::LISTEN_ERROR);}LOG(LogLevel::DEBUG) << "listen success";}std::shared_ptr<Socket> AcceptOrDie(InetAddr*client)override{struct sockaddr_in addr;socklen_t len = sizeof(addr);int id=accept(_sockid,(struct sockaddr*)&addr,&len);if(id<0){LOG(LogLevel::WARNING) << "accept error";return nullptr;}LOG(LogLevel::DEBUG) << "accept success";client->SetAddr(addr);return std::make_shared<TcpServerSocket>(id);}int Recv(std::string *out)override{char buff[4096*2];int n=recv(_sockid,buff,sizeof(buff),0);*out+=buff;return n;}void Send(std::string &message)override{send(_sockid,message.c_str(),message.size(),0);}void Close()override{close(_sockid);}~TcpServerSocket() {}private:int _sockid;};

Sock.hpp主要定义了Socket相关的封装,通过模板父类,使子类在继承时候能够直接调用公共方法,只需要对父类的纯虚函数进行重写。其中包括TcpServerSocket类的接口与实现,屏蔽了底层网络通信细节,提供统一的Accept、Recv、Send、Close等方法,便于上层应用快速构建TCP服务器。

InetAddr.hpp:

#pragma once#include <iostream>#include <sys/types.h>#include <sys/socket.h>#include <netinet/in.h> #include <arpa/inet.h>  #include <string.h>#include"Comd.hpp"class InetAddr{public://从网络接受sock地址,转成主机地址InetAddr(){}InetAddr(struct sockaddr_in&addr):_addr(addr){_port=ntohs(_addr.sin_port);char ipbuff[64];inet_ntop(AF_INET,&_addr.sin_addr,ipbuff,sizeof(ipbuff));_ip=ipbuff;}//本地转网络序列InetAddr(std::string ip,uint16_t port){_ip=ip;_port=port;memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;_addr.sin_port=htons(_port);inet_pton(AF_INET,_ip.c_str(),&_addr.sin_addr);}void SetAddr(struct sockaddr_in&addr){_addr=addr;_port=ntohs(_addr.sin_port);char ipbuff[64];inet_ntop(AF_INET,&_addr.sin_addr,ipbuff,sizeof(ipbuff));_ip=ipbuff;}std::string OutPut(){std::string gout="["+_ip+":"+std::to_string(_port)+"]";return gout;}socklen_t Lenth(){return sizeof(_addr);}sockaddr* InetAddrPtr(){return COV(_addr);}~InetAddr() {}private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;};

InetAddr.hpp主要定义了网络编程中用于封装IP地址与端口号的核心类InetAddr,提供IPv4地址的字符串与二进制格式转换功能。该类通过构造函数初始化地址信息,支持以标准形式输出“[IP:Port]”格式的字符串,常用于日志打印或连接标识。同时,SetAddr方法允许从sockaddr_in结构体反向填充对象字段,增强与其他系统调用的兼容性。其成员函数InetAddrPtr返回指向内部sockaddr结构的指针,便于底层网络接口传参,Lenth则固定返回sizeof(sockaddr_in),确保传输层操作时结构体长度正确。

TcpServer.hpp:

#pragma once#include <iostream>#include <memory>#include <functional>#include <unistd.h>#include "InetAddr.hpp"#include "Socket.hpp"#include <sys/wait.h>using ioservice = std::function<void(std::shared_ptr<Socket> &, InetAddr &)>;class TcpServer{public:TcpServer(uint16_t port): _port(port), _isruning(false),_listensock(std::make_shared<TcpServerSocket>()){_listensock->BuildTcpServerSocket(_port);}void Run(ioservice server){if (_isruning)return;_isruning = true;while (_isruning){InetAddr client;std::shared_ptr<Socket> sockid = _listensock->AcceptOrDie(&client);if(sockid==nullptr)continue;pid_t fid = fork();if (fid < 0){LOG(LogLevel::ERROR) << "Fork error";exit(Error::FORK_ERROR);}else if (fid == 0){if(fork()==0){_listensock->Close();server(sockid,client);}exit(Error::OK);}else{  sockid->Close();waitpid(fid, nullptr, 0);}}}~TcpServer() {}private:std::shared_ptr<Socket> _listensock;uint16_t _port;bool _isruning;};

TcpServer.hpp主要负责封装TCP服务器的核心功能,包括监听套接字的创建、绑定、监听以及客户端连接的处理。通过RAII机制管理资源,确保异常安全与对象生命周期的精确控制。类中定义了标准的服务器启动流程,结合多进程模型实现并发服务,每个新连接由独立子进程处理,避免相互干扰。

HTTP.hpp:

#pragma once#include <iostream>#include "InetAddr.hpp"#include "TcpServer.hpp"#include <unordered_map>#include "Tool.hpp"#include <sstream>const std::string entrance_root = "./wwwroot";const std::string firstindex = "index.html";const std::string errorindex = "404.html";const std::string gspace = " ";const std::string glinespace = "\r\n";const std::string gkvspace = ": ";// 将请求进行反序列化class HttpRequest{public:void Phase(std::string &line){std::stringstream ss(line);ss >> _method >> _url >> _version;}void Deserialization(std::string &message){std::string line;Tool::ReadOneLine(message, &line, glinespace);Phase(line);LOG(LogLevel::INFO) << "Method:" << _method;LOG(LogLevel::INFO) << "Url:" << _url;LOG(LogLevel::INFO) << "Version:" << _version;if (_url == "/") // 如果是默认则拼接首页{_url = entrance_root + _url + firstindex;}else{_url = entrance_root  + _url;}const std::string temp = "?";auto pos = _url.find(temp);if (pos == std::string::npos)return;// 是微服务_ismicroservices = true;_args = _url.substr(pos + temp.size());_url = _url.substr(0, pos);LOG(LogLevel::INFO) << "用户请求微服务" << _url << "参数为" << _args;}std::string URL(){return _url;}bool IsMicroservices(){return _ismicroservices;}std::string Args(){return _args;}private:std::string _method;std::string _url;std::string _version;std::unordered_map<std::string, std::string> _handle;std::string spaceline;std::string _args;bool _ismicroservices = false;};// 将数据进行序列化class HttpReSpoens{public:HttpReSpoens(): _version("HTTP/1.1"), _spaceline(glinespace){}std::string serialization(){std::string respones;respones += _version + gspace + std::to_string(_code) + gspace + _describe + glinespace;auto it = _handle.begin();std::string resp_header;while (it != _handle.end()){std::string handlest = it->first + gkvspace + it->second + glinespace;resp_header += handlest;++it;}respones += resp_header + _spaceline + _data;return respones;}void SetCode(int code){_code = code;switch (code){case 200:_describe = "OK"; // 成功break;case 301:_describe = "Moved Permanently"; // 永久重定向break;case 302:_describe = "Found"; // 临时重定向break;case 404:_describe = "Not Found"; // 资源未找到break;case 500:_describe = "Internal Server Error"; // 服务器内部错误break;default:_describe = "Unknown Status"; // 未知状态(兜底)break;}}void SetData(std::string data){_data = data;}void SetHandle(std::string key, std::string value){auto it = _handle.find(key);if (it != _handle.end())return;_handle.insert(std::make_pair(key, value));}void MakeType(){auto type = _targetfile.rfind("."); // 获取客户端请求数据的类型if (type == std::string::npos){_file_type= "text/html";}std::string filetype = _targetfile.substr(type);LOG(LogLevel::DEBUG)<<"解析出后缀格式为"<<filetype;if (filetype == ".html" || filetype == ".htm")_file_type = "text/html; charset=utf-8";else if (filetype == ".jpg")_file_type = "image/jpeg";else if (filetype == ".png")_file_type = "image/png";else if (filetype == ".mp3")_file_type = "audio/mp3";else if (filetype == ".mp4")_file_type = "video/mpeg4";else_file_type = "";}bool MakeResPones(){if (_targetfile == "./wwwroot/favicon.ico"){LOG(LogLevel::DEBUG) << "用户请求: " << _targetfile << "忽略它";SetCode(204);SetHandle("Content-Length", "0");return false;}MakeType();if (Tool::ReadFile_Text(_targetfile, &_data)){SetCode(200);LOG(LogLevel::INFO) << "client accept " << _targetfile;SetHandle("Content-Length", std::to_string(Tool::FileSize(_targetfile)));SetHandle("Content-Type", _file_type);return true;}else{SetCode(404);LOG(LogLevel::INFO) << "client accept " << _targetfile << "No Found";_targetfile = entrance_root + "/" + errorindex;LOG(LogLevel::DEBUG)<<"重新请求"<<_targetfile;MakeType();Tool::ReadFile_Text(_targetfile, &_data);SetHandle("Content-Length", std::to_string(Tool::FileSize(_targetfile)));SetHandle("Content-Type", _file_type);return true;}}void SetTargetfile(const std::string &targetfile){_targetfile = targetfile;}private:std::string _version;int _code;std::string _describe;std::unordered_map<std::string, std::string> _handle;std::string _spaceline;std::string _targetfile;std::string _data;std::string _file_type;};using minserver_func = std::function<void(HttpRequest &, HttpReSpoens &)>;class Http{public:Http(uint16_t port): _server(std::make_unique<TcpServer>(port)){}void Handler(std::shared_ptr<Socket> &sockid, InetAddr &client){std::string message;int n = sockid->Recv(&message);if (n > 0){HttpRequest requ;HttpReSpoens res;requ.Deserialization(message);if (requ.IsMicroservices()){LOG(LogLevel::DEBUG)<<"进入微服务处理逻辑";auto it = _minserver.find(requ.URL());if (it != _minserver.end()){LOG(LogLevel::DEBUG)<<"找到微服务"<<it->first<<"#";_minserver[requ.URL()](requ, res);}else{LOG(LogLevel::DEBUG)<<"没有该微服务"<<requ.URL();}}else{res.SetTargetfile(requ.URL());LOG(LogLevel::DEBUG) << client.OutPut() << "#请求URL" << requ.URL();res.MakeResPones();}std::string message = res.serialization();sockid->Send(message);}else if (n == 0){LOG(LogLevel::INFO) << client.OutPut() << "#Quit";return;}else{LOG(LogLevel::INFO) << client.OutPut() << "#Error";return;}}void Register(std::string method, minserver_func minserver){std::string key = entrance_root + method;auto it = _minserver.find(key);if (it != _minserver.end())return;_minserver.insert(std::make_pair(key, minserver));LOG(LogLevel::DEBUG) << "insert method:" << key;}void Start(){_server->Run([this](std::shared_ptr<Socket> &sockid, InetAddr &client){ this->Handler(sockid, client); });}private:std::unique_ptr<TcpServer> _server;std::unordered_map<std::string, minserver_func> _minserver;};

Http.hpp主要负责封装HTTP协议的请求与响应,解析请求行、请求头及请求体,并生成符合HTTP标准的响应报文,同时提供对状态码、MIME类型的自动映射与处理,支持静态资源的读取与动态数据的回调响应。通过流式解析避免内存溢出,提升服务端处理效率,在高并发场景下仍能保持稳定性能表现。

Tool.hpp

#pragma once#include<iostream>#include<fstream>#include<string>class Tool{public:static bool ReadOneLine(std::string &message,std::string *line,const std::string &sep){auto pos=message.find(sep);if(pos==std::string::npos)return false;*line=message.substr(0,pos);message=message.substr(0,pos+sep.size());return true;}static bool ReadFile_Text(const std::string filename,std::string*out){int filesize=FileSize(filename);if(filesize<0)return false;std::ifstream in(filename);out->resize(filesize);in.read((char*)out->c_str(),filesize);in.close();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 size=in.tellg();in.seekg(0,in.beg);in.close();return size;}};

Tool.hpp主要负责封装常用工具函数,便于项目中多处调用时保持代码简洁与一致性。

http://www.dtcms.com/a/557240.html

相关文章:

  • linux实现设备驱动-字符型设备驱动
  • 门户网站排版有引导的网站
  • Linux USB 子系统深度解析
  • Linux time function in C/C++【2】
  • 人工智能学习中深度学习之python基础之迭代器、生成器、文件处理和模块等
  • wordpress显示评论数福建企业seo推广
  • 12.C++:模版进阶
  • 大模型训练评估中的交叉验证详解
  • 变更股东怎样在工商网站做公示做网站的收费标准
  • (142页PPT)立白MES解决方案1Wonderware运营管理平台(附下载方式)
  • 机器学习日报10
  • Linux 2.6.10 调度器负载均衡机制深度解析:从理论到实现
  • 访链家网网站开发嘉定房地产网站建设
  • 多商户商城APP源码开发的未来方向:云原生、电商中台与智能客服
  • Liunx线程安全
  • 基于数据增强与对抗学习的门诊电子病历(EMR)文本分类python编程
  • 企业网站seo推广技巧建设视频网站设计意义
  • VSCode的插件配置同步到gitee
  • 短剧广告联盟 APP 用户画像:基于观看行为的广告精准投放模型
  • 找快照网站查询网站建设博采
  • [论文阅读] AI+ | AI如何重塑审计行业?从“手工筛查”到“智能决策”:AI审计的核心逻辑与未来路径
  • 论文精读:A review on multi-view learning(多视图学习综述)
  • 长宁制作网站网站建设属于会计哪个科目
  • 波动率建模(三)Heston模型及其Python实现
  • 左侧 导航 网站泰安信誉好的网络推广公司
  • python 初学2
  • 51单片机基础-LCD12864液晶显示
  • 在openSUSE-Leap-15.6-DVD-x86_64-Media自制应用软件离线包——备份91个视频解码器的rpm包
  • 《云原生基础设施》
  • dedecms手机网站开发怎么在网站底部添加备案号