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

应用层协议 HTTP

HTTP 协议


虽然我们说, 应用层协议是我们程序猿自己定的. 但实际上, 已经有大佬们定义了一些现成的, 又非常好用的应用层协议, 供我们直接参考使用. HTTP(超文本传输协议)就是其中之一。

在互联网世界中,HTTP(HyperText Transfer Protocol,超文本传输协议)是一个至关重要的协议。它定义了客户端(如浏览器)与服务器之间如何通信,以交换或传输超文本(如 HTML 文档)。
HTTP 协议是客户端与服务器之间通信的基础。客户端通过 HTTP 协议向服务器发送请求,服务器收到请求后处理并返回响应。HTTP 协议是一个无连接、无状态的协议,即每次请求都需要建立新的连接,且服务器不会保存客户端的状态信息。

认识 URL


平时我们俗称的 "网址" 其实就是说的 URL

urlencode 和 urldecode


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

"+" 被转义成了 "%2B",urldecode 就是 urlencode 的逆过程;

HTTP 协议请求与响应格式

HTTP 请求

首行: [方法] + [url] + [版本]

Header: 请求的属性, 冒号分割的键值对;每组属性之间使用\r\n 分隔;遇到空行表示 Header 部分结束

Body: 空行后面的内容都是 Body. Body 允许为空字符串. 如果 Body 存在, 则在Header 中会有一个 Content-Length 属性来标识 Body 的长度;

基本的应答格式

所以http请求是一个大号的字符串

编写 HTTP 请求的代码 - 验证 http 请求

公共代码

Socket.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <unistd.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"namespace SocketModule
{using namespace LogModule;const static int gbacklog =16;const  static int defaultfd =-1;// 基类socketclass Socket{public:virtual ~Socket() {}virtual void SocketOrDie() = 0;virtual void BindOrDie(uint16_t port) = 0;virtual void ListenOrDie(int backlog) = 0;virtual  std::shared_ptr<Socket> Accept(InetAddr * client)= 0;virtual void Close()=0;virtual int Recv(std::string * out) = 0;virtual int Send(std::string  &message) = 0;virtual int Connect(const std::string &server_ip ,uint16_t port) =0;public:void BuildTcpClientSocketMethod(){SocketOrDie();}void BUildTcpLIstenSocketMethod(uint16_t port,int backlog = gbacklog){SocketOrDie();BindOrDie(port);ListenOrDie(backlog);}// void BUildUdpSocketMethod()// {//     SocketOrDie();//     BindOrDie();// }};class TcpSocket : public Socket{public:TcpSocket():_sockfd(defaultfd){}TcpSocket(int fd): _sockfd(fd) {}~TcpSocket() {}void SocketOrDie() override{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(Loglevel::FATAL) << "创建套接字失败!";exit(SOCKET_ERR);}LOG(Loglevel::INIF) << "创建套接字成功!";}void BindOrDie(uint16_t port) override{InetAddr localaddr(port);int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());if (n < 0){LOG(Loglevel::FATAL) << "绑定失败!";exit(BIND_ERR);}LOG(Loglevel::INIF) << "绑定成功!";}void ListenOrDie(int backlog) override{int n = ::listen(_sockfd, backlog);if (n < 0){LOG(Loglevel::FATAL) << "监听失败!";exit(LISTEN_ERR);}LOG(Loglevel::INIF) << "监听成功!";}std::shared_ptr<Socket> Accept(InetAddr * client) override{struct sockaddr_in peer;socklen_t len  = sizeof(peer);int fd =::accept(_sockfd,CONV(peer),&len);if (fd < 0){LOG(Loglevel::WARNING)<<"连接失败!";exit(-1);}LOG(Loglevel::WARNING)<<"连接成功!";client->SetAddr(peer);return  std::make_shared<TcpSocket>(fd);}void Close() override{if(_sockfd >=0){::close(_sockfd);}}int Recv(std::string * out) override{//流式读取,不关心读到的是什么char buffer[4096];ssize_t n =::recv(_sockfd,buffer,sizeof(buffer)-1,0);if (n >0){buffer[n]=0;*out+=buffer;return n;}return n;}int Send(std::string  &message) override{return send(_sockfd,message.c_str(),message.size(),0);}int Connect(const std::string &server_ip ,uint16_t port) override{InetAddr server(server_ip,port);return ::connect(_sockfd,server.NetAddrPtr(),server.NetAddrLen()) ;    }private:int _sockfd; //};// class UdpSocket : public Socket// {// };
}

Mutex.hpp

#pragma once
#include <pthread.h>
#include <iostream>
namespace MutexModule
{ class Mutex{public:Mutex(){pthread_mutex_init(&_mutex, nullptr);}void Lock(){int n = pthread_mutex_lock(&_mutex);(void)n;}void Unlock(){int n = pthread_mutex_unlock(&_mutex);(void)n;}~Mutex(){pthread_mutex_destroy(&_mutex);}pthread_mutex_t *get(){return &_mutex;}private:pthread_mutex_t _mutex;};class LockGuard{public:LockGuard(Mutex &mutex):_mutex(mutex){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex &_mutex;};
}

Log.hpp

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
#include<ctime>namespace LogModule
{const std::string sep = "\r\n";using namespace  MutexModule ;// 2.刷新策略class LogStrategy{public:~LogStrategy() = default;virtual void SyncLog(const std::string &message) = 0;};// 显示器刷新日志的策略class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy() {}~ConsoleLogStrategy() {}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << sep;}private:Mutex _mutex;};// 缺省文件路径以及文件本身const std::string defaultpath = "./log";const std::string defaultfile = "my.log";// 文件刷新日志的策略class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path), _file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)) // 判断路径是否存在{return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;std::ofstream out(filename, std::ios::app); // 追加写入if (!out.is_open()){return;}out << message << sep;out.close();}~FileLogStrategy() {}private:Mutex _mutex;std::string _path; // 日志文件的路径std::string _file; // 要打印的日志文件};// 形成日志等级enum class Loglevel{DEBUG,INIF,WARNING,ERROR,FATAL};std::string Level2Str(Loglevel level){switch (level){case Loglevel::DEBUG:return "DEBUG";case Loglevel::INIF:return "INIF";case Loglevel::WARNING:return "WARNING";case Loglevel::ERROR:return "ERROR";case Loglevel::FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetTimeStamp(){time_t cuur =time(nullptr);struct tm curr_tm;localtime_r(&cuur,&curr_tm);char buffer[128];snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year+1900,curr_tm.tm_mon+1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return buffer;}class Logger{public:Logger(){EnableConsoleLogStrategy();}// 选择某种策略// 1.文件void EnableFileLogStrategy(){_ffush_strategy = std::make_unique<FileLogStrategy>();}// 显示器void EnableConsoleLogStrategy(){_ffush_strategy = std::make_unique<ConsoleLogStrategy>();}// 表示的是未来的一条日志class LogMessage{public:LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()), _level(level), _pid(getpid()), _src_name(src_name), _line_number(line_number), _logger(logger){// 合并左半部分std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << Level2Str(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage &operator<<(const T &info){// 右半部分,可变std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if (_logger._ffush_strategy){_logger._ffush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time; // 日志时间Loglevel _level;        // 日志状态pid_t _pid;             // 进程pidstd::string _src_name;  // 文件名称int _line_number;       // 对应的行号std::string _loginfo;   // 合并之后的一条完整信息Logger &_logger;};LogMessage operator()(Loglevel level, std::string src_name, int line_number){return LogMessage(level, src_name, line_number, *this);}~Logger() {}private:std::unique_ptr<LogStrategy> _ffush_strategy;};//全局日志对象Logger logger;//使用宏,简化用户操作,获取文件名和行号// __FILE__  一个宏,替换完成后目标文件的文件名// __LINE__  一个宏,替换完成后目标文件对应的行号#define LOG(level) logger(level,__FILE__,__LINE__) #define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()#define Enable_File_Log_Strategy()    logger.EnableFileLogStrategy()}#endif

Main.cc

#include"Http.hpp"// http port
int main(int argc,char* argv[])
{if(argc != 2){std::cout << "Usage: "<<argv[0]<<" port"<<std::endl;exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<Http> httpsvr = std::make_unique<Http>(port);httpsvr->Start();return 0;
}

Common.hpp

#pragma once#include <iostream>
#include<functional>
#include <string>
#include <cstring>
#include <memory>
#include<unistd.h>
#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
};class NoCopy
{public:
NoCopy(){}
~NoCopy(){}
NoCopy(const NoCopy&)=delete;
const NoCopy& operator= (const NoCopy &)=delete;
};#define CONV(addr) ((struct sockaddr *)&addr)

InetAddr.hpp

#pragma once
#include "Common.hpp"class InetAddr
{public:InetAddr(){}InetAddr(struct sockaddr_in &addr){   SetAddr(addr);}InetAddr(const std::string &ip,uint16_t port):_ip(ip),_port(port){//主机转网络memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;inet_pton(AF_INET,_ip.c_str(),&_addr.sin_addr);_addr.sin_port=htons(_port);}InetAddr(uint16_t port):_port(port),_ip("0"){   //端口转memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;_addr.sin_addr.s_addr=INADDR_ANY;_addr.sin_port = htons(_port);}uint16_t Port() { return _port; }std::string Ip() { return _ip; }void SetAddr(struct sockaddr_in &addr) {  //网络转主机_addr=addr;_port = ntohs(addr.sin_port);//_ip = inet_ntoa(addr.sin_addr);char ipbuffer[64];inet_ntop(AF_INET,&_addr.sin_addr,ipbuffer,sizeof(_addr));_ip=ipbuffer;}const struct sockaddr_in &NetAddr() { return _addr; }const struct sockaddr *NetAddrPtr() { return CONV(_addr); }socklen_t NetAddrLen() { return sizeof(_addr); }bool operator==(const InetAddr &addr){return addr._ip == _ip && addr._port == _port;}std::string StringAddr(){return _ip + ":" + std::to_string(_port);}~InetAddr() {}
private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};

 Tcpserver.hpp

#include "Socket.hpp"
#include<iostream>
#include<memory>
#include <sys/wait.h>
#include <functional>using namespace LogModule;
using namespace SocketModule;using ioservice_t = std::function<void(std::shared_ptr<Socket>&sock,InetAddr &client)>;class TcpServer
{public:
TcpServer(uint16_t port)
:_port(port)
,_listensockptr(std::make_unique<TcpSocket>())
,_isrunning(false)
{_listensockptr->BUildTcpLIstenSocketMethod(_port);
}
void Start(ioservice_t callback)
{_isrunning =true;while(_isrunning){InetAddr client;auto sock = _listensockptr->Accept(&client);if(sock == nullptr){continue;}LOG(Loglevel::DEBUG)<<"accept is running"<<client.StringAddr();//sock && clientpid_t id =fork();if (id < 0){LOG(Loglevel::FATAL)<<"创建子进程失败";exit(FORK_ERR);}else if(id == 0){//子进程 关闭listensock_listensockptr->Close();if(fork()>0){exit(0);}//孙子进程是孤儿进程callback(sock,client);exit(OK);}else{//父进程 关闭socksock->Close();pid_t rid = ::waitpid(id,nullptr,0);(void)rid;}}_isrunning =false;}
~TcpServer(){}
private:
uint16_t _port;
std::unique_ptr<Socket>_listensockptr;
bool _isrunning;
};

 Makefile

myhttp:Main.ccg++ -o $@ $^ -std=c++17
.PHONY:clean
clean:rm -f myhttp

Http.hpp

#pragma once
#include "TcpServer.hpp"
#include <memory>
#include <iostream>
#include <string>
#include <unordered_map>using namespace SocketModule;const std::string gspace = " ";
const std::string glinespace = "\r\n";
const std::string glinesep = ": ";class HttpRequest
{
public:std::string Serialize(){return std::string();}bool Deserialize(std::string &reqstr){return true;}HttpRequest() {}~HttpRequest() {}private:std::string _method;std::string _url;std::string _version;std::unordered_map<std::string, std::string> _headers;std::string _blankline;std::string _text;
};class HttpResponse
{public:std::string Serialize(){std::string status_line = _version +" "+gspace+std::to_string(_code)+gspace+_desc+glinespace;std::string resp_header;for(auto &header : _headrs){std::string line = header.first+glinesep+header.second+glinespace;resp_header+=line;}return status_line+resp_header+_blankline+_text;}bool Deserialize(std::string &reqstr){return true;}HttpResponse():_blankline(glinespace){}~HttpResponse(){}std::string _version; //版本
int _code; //状态码
std::string _desc; //状态码描述
std::unordered_map<std::string,std::string> _headrs;//相应报头
std::string _blankline;//空行
std::string _text;//正文
};class Http
{
public:Http(uint16_t port): tsvrp(std::make_unique<TcpServer>(port)){}void HandlenHttpRequest(std::shared_ptr<Socket> &sock, InetAddr &client){#ifndef DEBUG#define DEBUGstd::string httprequest;sock->Recv(&httprequest);std::cout<<httprequest<<std::endl;//构建应答,内存级 + 固定HttpResponse resp;resp._version = "HTTP?1.1";resp._code = 200;//正常resp._desc ="OK";resp._text ="<!DOCTYPE html>\<html lang=\"en\">\<head>\<meta charset=\"UTF-8\">\<meta name=\"viewport\" content=\"width=device-width, initial-scale=1.0\">\<title>Hello World</title>\</head>\<body>\<h1>Hello World</h1>\</body>\</html>";std::string response_str = resp.Serialize();sock->Send(response_str);#endif}void Start(){tsvrp->Start([this](std::shared_ptr<Socket> &sock, InetAddr &client){ this->HandlenHttpRequest(sock, client); });}~Http() {}private:std::unique_ptr<TcpServer> tsvrp;
};

这是浏览器返回的部分http

respones返回一个html

 HTTP 的方法

其中最常用的就是 GET 方法和 POST 方法.

HTTP 常见方法


1.GET 方法(重点)


用途:用于请求 URL 指定的资源。
示例:GET /index.html HTTP/1.1
特性:指定资源经服务器端解析后返回响应内容。
form 表单:https://www.runoob.com/html/html-forms.html

2.POST 方法(重点)


用途:用于传输实体的主体,通常用于提交表单数据。
示例:POST /submit.cgi HTTP/1.1
特性:可以发送大量的数据给服务器,并且数据包含在请求体中。
form 表单:https://www.runoob.com/html/html-forms.html

3.PUT 方法(不常用)


用途:用于传输文件,将请求报文主体中的文件保存到请求 URL 指定的位置。
示例:PUT /example.html HTTP/1.1
特性:不太常用,但在某些情况下,如 RESTful API 中,用于更新资源。


4. HEAD 方法


用途:与 GET 方法类似,但不返回报文主体部分,仅返回响应头。
示例:HEAD /index.html HTTP/1.1
特性:用于确认 URL 的有效性及资源更新的日期时间等。

5. DELETE 方法(不常用)


用途:用于删除文件,是 PUT 的相反方法。
示例:DELETE /example.html HTTP/1.1
特性:按请求 URL 删除指定的资源。


6. OPTIONS 方法


用途:用于查询针对请求 URL 指定的资源支持的方法。
示例:OPTIONS * HTTP/1.1
特性:返回允许的方法,如 GET、POST 等。

HttpDamon 

Util.hpp

#pragma once
#include <iostream>
#include <string>
#include <fstream>class Util
{public://打开指定文件
static bool ReadFileContent(const std::string &filename,std::string *out){//version1std::ifstream in(filename);if(!in.is_open()){return false;}std::string line;while(std::getline(in,line)){*out+=line;}in.close();return true;}
};

index.html

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

Common.hpp

#pragma once#include <iostream>
#include<functional>
#include <string>
#include <cstring>
#include <memory>
#include<unistd.h>
#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
};class NoCopy
{public:
NoCopy(){}
~NoCopy(){}
NoCopy(const NoCopy&)=delete;
const NoCopy& operator= (const NoCopy &)=delete;
};#define CONV(addr) ((struct sockaddr *)&addr)

Http.hpp

#pragma once
#include "TcpServer.hpp"
#include <memory>
#include <iostream>
#include <string>
#include <unordered_map>
#include"Util.hpp"using namespace SocketModule;const std::string gspace = " ";
const std::string glinespace = "\r\n";
const std::string glinesep = ": ";const std::string webroot = "./wwwroot";
const std::string homepage = "/index.html";
class HttpRequest
{
public:std::string Serialize(){return std::string();}bool Deserialize(std::string &reqstr){return true;}HttpRequest() {}~HttpRequest() {}private:std::string _method;std::string _url;std::string _version;std::unordered_map<std::string, std::string> _headers;std::string _blankline;std::string _text;
};class HttpResponse
{public:std::string Serialize(){std::string status_line = _version +" "+gspace+std::to_string(_code)+gspace+_desc+glinespace;std::string resp_header;for(auto &header : _headrs){std::string line = header.first+glinesep+header.second+glinespace;resp_header+=line;}return status_line+resp_header+_blankline+_text;}bool Deserialize(std::string &reqstr){return true;}HttpResponse():_blankline(glinespace){}~HttpResponse(){}std::string _version; //版本
int _code; //状态码
std::string _desc; //状态码描述
std::unordered_map<std::string,std::string> _headrs;//相应报头
std::string _blankline;//空行
std::string _text;//正文
};class Http
{
public:Http(uint16_t port): tsvrp(std::make_unique<TcpServer>(port)){}void HandlenHttpRequest(std::shared_ptr<Socket> &sock, InetAddr &client){#ifndef DEBUG#define DEBUGstd::string httprequest;sock->Recv(&httprequest);std::cout<<httprequest<<std::endl;//构建应答,内存级 + 固定HttpResponse resp;resp._version = "HTTP/1.1";resp._code = 200;//正常resp._desc ="OK";std::string filename= webroot+homepage;// ./wwwroot/index.htmlbool res = Util::ReadFileContent(filename,&(resp._text));(void)res;std::string response_str = resp.Serialize();sock->Send(response_str);#endif}void Start(){tsvrp->Start([this](std::shared_ptr<Socket> &sock, InetAddr &client){ this->HandlenHttpRequest(sock, client); });}~Http() {}private:std::unique_ptr<TcpServer> tsvrp;
};

InerAddr.hpp

#pragma once
#include "Common.hpp"class InetAddr
{public:InetAddr(){}InetAddr(struct sockaddr_in &addr){   SetAddr(addr);}InetAddr(const std::string &ip,uint16_t port):_ip(ip),_port(port){//主机转网络memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;inet_pton(AF_INET,_ip.c_str(),&_addr.sin_addr);_addr.sin_port=htons(_port);}InetAddr(uint16_t port):_port(port),_ip("0"){   //端口转memset(&_addr,0,sizeof(_addr));_addr.sin_family=AF_INET;_addr.sin_addr.s_addr=INADDR_ANY;_addr.sin_port = htons(_port);}uint16_t Port() { return _port; }std::string Ip() { return _ip; }void SetAddr(struct sockaddr_in &addr) {  //网络转主机_addr=addr;_port = ntohs(addr.sin_port);//_ip = inet_ntoa(addr.sin_addr);char ipbuffer[64];inet_ntop(AF_INET,&_addr.sin_addr,ipbuffer,sizeof(_addr));_ip=ipbuffer;}const struct sockaddr_in &NetAddr() { return _addr; }const struct sockaddr *NetAddrPtr() { return CONV(_addr); }socklen_t NetAddrLen() { return sizeof(_addr); }bool operator==(const InetAddr &addr){return addr._ip == _ip && addr._port == _port;}std::string StringAddr(){return _ip + ":" + std::to_string(_port);}~InetAddr() {}
private:struct sockaddr_in _addr;std::string _ip;uint16_t _port;
};

Log.hpp

#ifndef __LOG_HPP__
#define __LOG_HPP__#include <iostream>
#include <string>
#include "Mutex.hpp"
#include <filesystem>
#include <fstream>
#include <memory>
#include <unistd.h>
#include <sstream>
#include<ctime>namespace LogModule
{const std::string sep = "\r\n";using namespace  MutexModule ;// 2.刷新策略class LogStrategy{public:~LogStrategy() = default;virtual void SyncLog(const std::string &message) = 0;};// 显示器刷新日志的策略class ConsoleLogStrategy : public LogStrategy{public:ConsoleLogStrategy() {}~ConsoleLogStrategy() {}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::cout << message << sep;}private:Mutex _mutex;};// 缺省文件路径以及文件本身const std::string defaultpath = "./log";const std::string defaultfile = "my.log";// 文件刷新日志的策略class FileLogStrategy : public LogStrategy{public:FileLogStrategy(const std::string &path = defaultpath, const std::string &file = defaultfile): _path(path), _file(file){LockGuard lockguard(_mutex);if (std::filesystem::exists(_path)) // 判断路径是否存在{return;}try{std::filesystem::create_directories(_path);}catch (const std::filesystem::filesystem_error &e){std::cerr << e.what() << '\n';}}void SyncLog(const std::string &message) override{LockGuard lockguard(_mutex);std::string filename = _path + (_path.back() == '/' ? "" : "/") + _file;std::ofstream out(filename, std::ios::app); // 追加写入if (!out.is_open()){return;}out << message << sep;out.close();}~FileLogStrategy() {}private:Mutex _mutex;std::string _path; // 日志文件的路径std::string _file; // 要打印的日志文件};// 形成日志等级enum class Loglevel{DEBUG,INIF,WARNING,ERROR,FATAL};std::string Level2Str(Loglevel level){switch (level){case Loglevel::DEBUG:return "DEBUG";case Loglevel::INIF:return "INIF";case Loglevel::WARNING:return "WARNING";case Loglevel::ERROR:return "ERROR";case Loglevel::FATAL:return "FATAL";default:return "UNKNOWN";}}std::string GetTimeStamp(){time_t cuur =time(nullptr);struct tm curr_tm;localtime_r(&cuur,&curr_tm);char buffer[128];snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d",curr_tm.tm_year+1900,curr_tm.tm_mon+1,curr_tm.tm_mday,curr_tm.tm_hour,curr_tm.tm_min,curr_tm.tm_sec);return buffer;}class Logger{public:Logger(){EnableConsoleLogStrategy();}// 选择某种策略// 1.文件void EnableFileLogStrategy(){_ffush_strategy = std::make_unique<FileLogStrategy>();}// 显示器void EnableConsoleLogStrategy(){_ffush_strategy = std::make_unique<ConsoleLogStrategy>();}// 表示的是未来的一条日志class LogMessage{public:LogMessage(Loglevel &level, std::string &src_name, int line_number, Logger &logger): _curr_time(GetTimeStamp()), _level(level), _pid(getpid()), _src_name(src_name), _line_number(line_number), _logger(logger){// 合并左半部分std::stringstream ss;ss << "[" << _curr_time << "] "<< "[" << Level2Str(_level) << "] "<< "[" << _pid << "] "<< "[" << _src_name << "] "<< "[" << _line_number << "] "<< "- ";_loginfo = ss.str();}template <typename T>LogMessage &operator<<(const T &info){// 右半部分,可变std::stringstream ss;ss << info;_loginfo += ss.str();return *this;}~LogMessage(){if (_logger._ffush_strategy){_logger._ffush_strategy->SyncLog(_loginfo);}}private:std::string _curr_time; // 日志时间Loglevel _level;        // 日志状态pid_t _pid;             // 进程pidstd::string _src_name;  // 文件名称int _line_number;       // 对应的行号std::string _loginfo;   // 合并之后的一条完整信息Logger &_logger;};LogMessage operator()(Loglevel level, std::string src_name, int line_number){return LogMessage(level, src_name, line_number, *this);}~Logger() {}private:std::unique_ptr<LogStrategy> _ffush_strategy;};//全局日志对象Logger logger;//使用宏,简化用户操作,获取文件名和行号// __FILE__  一个宏,替换完成后目标文件的文件名// __LINE__  一个宏,替换完成后目标文件对应的行号#define LOG(level) logger(level,__FILE__,__LINE__) #define Enable_Console_Log_Strategy() logger.EnableConsoleLogStrategy()#define Enable_File_Log_Strategy()    logger.EnableFileLogStrategy()}#endif

Main.cc

#include"Http.hpp"// http port
int main(int argc,char* argv[])
{if(argc != 2){std::cout << "Usage: "<<argv[0]<<" port"<<std::endl;exit(USAGE_ERR);}uint16_t port = std::stoi(argv[1]);std::unique_ptr<Http> httpsvr = std::make_unique<Http>(port);httpsvr->Start();return 0;
}

Makefile

myhttp:Main.ccg++ -o $@ $^ -std=c++17
.PHONY:clean
clean:rm -f myhttp

Mutex.hpp

#pragma once
#include <pthread.h>
#include <iostream>
namespace MutexModule
{ class Mutex{public:Mutex(){pthread_mutex_init(&_mutex, nullptr);}void Lock(){int n = pthread_mutex_lock(&_mutex);(void)n;}void Unlock(){int n = pthread_mutex_unlock(&_mutex);(void)n;}~Mutex(){pthread_mutex_destroy(&_mutex);}pthread_mutex_t *get(){return &_mutex;}private:pthread_mutex_t _mutex;};class LockGuard{public:LockGuard(Mutex &mutex):_mutex(mutex){_mutex.Lock();}~LockGuard(){_mutex.Unlock();}private:Mutex &_mutex;};
}

Socket.hpp

#pragma once
#include <iostream>
#include <string>
#include <sys/socket.h>
#include <sys/types.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <cstdlib>
#include <unistd.h>
#include "Log.hpp"
#include "Common.hpp"
#include "InetAddr.hpp"namespace SocketModule
{using namespace LogModule;const static int gbacklog =16;const  static int defaultfd =-1;// 基类socketclass Socket{public:virtual ~Socket() {}virtual void SocketOrDie() = 0;virtual void BindOrDie(uint16_t port) = 0;virtual void ListenOrDie(int backlog) = 0;virtual  std::shared_ptr<Socket> Accept(InetAddr * client)= 0;virtual void Close()=0;virtual int Recv(std::string * out) = 0;virtual int Send(std::string  &message) = 0;virtual int Connect(const std::string &server_ip ,uint16_t port) =0;public:void BuildTcpClientSocketMethod(){SocketOrDie();}void BUildTcpLIstenSocketMethod(uint16_t port,int backlog = gbacklog){SocketOrDie();BindOrDie(port);ListenOrDie(backlog);}// void BUildUdpSocketMethod()// {//     SocketOrDie();//     BindOrDie();// }};class TcpSocket : public Socket{public:TcpSocket():_sockfd(defaultfd){}TcpSocket(int fd): _sockfd(fd) {}~TcpSocket() {}void SocketOrDie() override{_sockfd = ::socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(Loglevel::FATAL) << "创建套接字失败!";exit(SOCKET_ERR);}LOG(Loglevel::INIF) << "创建套接字成功!";}void BindOrDie(uint16_t port) override{InetAddr localaddr(port);int n = ::bind(_sockfd, localaddr.NetAddrPtr(), localaddr.NetAddrLen());if (n < 0){LOG(Loglevel::FATAL) << "绑定失败!";exit(BIND_ERR);}LOG(Loglevel::INIF) << "绑定成功!";}void ListenOrDie(int backlog) override{int n = ::listen(_sockfd, backlog);if (n < 0){LOG(Loglevel::FATAL) << "监听失败!";exit(LISTEN_ERR);}LOG(Loglevel::INIF) << "监听成功!";}std::shared_ptr<Socket> Accept(InetAddr * client) override{struct sockaddr_in peer;socklen_t len  = sizeof(peer);int fd =::accept(_sockfd,CONV(peer),&len);if (fd < 0){LOG(Loglevel::WARNING)<<"连接失败!";exit(-1);}LOG(Loglevel::WARNING)<<"连接成功!";client->SetAddr(peer);return  std::make_shared<TcpSocket>(fd);}void Close() override{if(_sockfd >=0){::close(_sockfd);}}int Recv(std::string * out) override{//流式读取,不关心读到的是什么char buffer[4096];ssize_t n =::recv(_sockfd,buffer,sizeof(buffer)-1,0);if (n >0){buffer[n]=0;*out+=buffer;return n;}return n;}int Send(std::string  &message) override{return send(_sockfd,message.c_str(),message.size(),0);}int Connect(const std::string &server_ip ,uint16_t port) override{InetAddr server(server_ip,port);return ::connect(_sockfd,server.NetAddrPtr(),server.NetAddrLen()) ;    }private:int _sockfd; //};// class UdpSocket : public Socket// {// };
}

TcpServer.hpp

#include "Socket.hpp"
#include<iostream>
#include<memory>
#include <sys/wait.h>
#include <functional>using namespace LogModule;
using namespace SocketModule;using ioservice_t = std::function<void(std::shared_ptr<Socket>&sock,InetAddr &client)>;class TcpServer
{public:
TcpServer(uint16_t port)
:_port(port)
,_listensockptr(std::make_unique<TcpSocket>())
,_isrunning(false)
{_listensockptr->BUildTcpLIstenSocketMethod(_port);
}
void Start(ioservice_t callback)
{_isrunning =true;while(_isrunning){InetAddr client;auto sock = _listensockptr->Accept(&client);if(sock == nullptr){continue;}LOG(Loglevel::DEBUG)<<"accept is running"<<client.StringAddr();//sock && clientpid_t id =fork();if (id < 0){LOG(Loglevel::FATAL)<<"创建子进程失败";exit(FORK_ERR);}else if(id == 0){//子进程 关闭listensock_listensockptr->Close();if(fork()>0){exit(0);}//孙子进程是孤儿进程callback(sock,client);exit(OK);}else{//父进程 关闭socksock->Close();pid_t rid = ::waitpid(id,nullptr,0);(void)rid;}}_isrunning =false;}
~TcpServer(){}
private:
uint16_t _port;
std::unique_ptr<Socket>_listensockptr;
bool _isrunning;
};

http://www.xdnf.cn/news/14999.html

相关文章:

  • Thrust库介绍与使用
  • LangGraph-2-interrupt 流程中断
  • Language Models are Few-Shot Learners: 开箱即用的GPT-3(二)
  • MacOS 终端(Terminal)配置显示日期时间
  • 亚矩阵云手机破解Maio广告平台多账号风控:从“生存焦虑”到“规模化增长”的终极方案
  • OneFileLLM:一键聚合多源信息流
  • AI探索 | 豆包智能助手跟扣子空间(AI办公助手)有什么区别
  • [Meetily后端框架] AI摘要结构化 | `SummaryResponse`模型 | Pydantic库 | vs marshmallow库
  • Qt Creator控件及其用途详细总结
  • CH9121T电路及配置详解
  • AI驱动的业务系统智能化转型:从非结构化到结构化的智能转换
  • 【深度学习新浪潮】什么是持续预训练?
  • 从零开始搭建深度学习大厦系列-2.卷积神经网络基础(5-9)
  • C++类对象多态底层原理及扩展问题
  • Excalidraw:一款轻量、高效、极具手感的在线白板工具
  • 18th Day| 654.最大二叉树, 617.合并二叉树, 700.二叉搜索树中的搜索,98.验证二叉搜索树
  • 微算法科技的前沿探索:量子机器学习算法在视觉任务中的革新应用
  • 虚拟储能与分布式光伏协同优化:新型电力系统的灵活性解决方案
  • Mac自定义右键功能
  • ThinkBook 14s IWL(20RM)OEM系统镜像原厂Win10系统
  • @Schema是什么?
  • C++之string类的实现代码及其详解(下)
  • Flowable21条件事件------------持续更新中
  • 【Linux手册】从接口到管理:Linux文件系统的核心操作指南
  • 《C++初阶之内存管理》【内存分布 + operator new/delete + 定位new】
  • 访问Windows服务器备份SQL SERVER数据库
  • AI【应用 03】Windows环境部署 TTS CosyVoice2.0 详细流程记录(Matcha-TTS、spk2info.pt等文件分享)
  • 从品牌附庸到自我表达:定制开发开源AI智能名片S2B2C商城小程序赋能下的营销变革
  • iOS 抓包详细教程:从零搭建、操作到实战调试的全流程指南
  • Fiddler中文版全面评测:功能亮点、使用场景与中文网资源整合指南