在这里插入图片描述

目录

  • 一、认识URL
  • 二、urlencode和urldecode
  • 三、HTTP协议格式(使用Fiddler抓包)
    • 3.1 安装并使用Fiddler抓包
    • 3.2 HTTP协议格式
      • 3.2.1 HTTP请求
        • 3.2.1.1 资源URL路径
        • 3.2.1.2 请求方法(Method)
        • 3.2.1.3 Location头字段(重定向相关)
      • 3.2.2 HTTP响应
        • 3.2.2.1 状态码和状态码描述
      • 3.2.3 请求与响应共有字段
        • 3.2.3.1 HTTP协议版本
        • 3.2.3.2 Content-Type
      • 3.2.4 Cookie 和 Session
        • 3.2.4.1 Cookie
        • 3.2.4.2 Session
  • 四、实现一个简单的HTTP服务器
    • 4.1 Socket.hpp(封装套接字)
    • 4.2 HttpProtocol.hpp(自定义HTTP协议)
    • 4.3 TcpServer.hpp(服务端封装)
    • 4.4 wwwroot/index.html(主页)
    • 4.5 wwwroot/login.html(登录页面)
    • 4.6 wwwroot/404.html(404页面、网上搜)
    • 4.7 Main.cpp(服务端)
  • 结尾

一、认识URL

平时我们俗称的 “网址” 其实就是说的 URL
在这里插入图片描述
域名会通过DNS被解析为服务器的IP地址。但是现在的服务器不允许用户直接通过IP地址请求服务。

在这里插入图片描述


二、urlencode和urldecode

像 / ? : 等这样的字符,已经被url当做特殊意义理解了。所以这些字符不能随意出现。
例如,某个参数中需要带有这些特殊字符,就必须先对特殊字符进行转义。

转义的规则如下:
将需要转码的字符转为16进制,然后从右到左,取4位(不足4位直接处理),每2位做一位,前面加上%,编码成%XY格式。

在这里插入图片描述

“+” 被转义成了 “%2B”

urldecode就是urlencode的逆过程。


三、HTTP协议格式(使用Fiddler抓包)

3.1 安装并使用Fiddler抓包

Fiddler 下载地址:https://www.telerik.com/download/fiddler

这里我们使用软件 Fiddler 进行抓包,刚刚安装的软件无法对HTTPS进行抓包,如果需要对HTTPS进行抓包,需要进行以下操作。

在这里插入图片描述

在这里插入图片描述

接下来我们就可以看一下HTTP协议的格式了。

在这里插入图片描述


3.2 HTTP协议格式

将上面的内容进行提取就是下面图片中的内容了。这里我只提取了部分内容。

在这里插入图片描述


3.2.1 HTTP请求

  • 提到请求报文来说,我们需要解决以下问题:

    1. 报头和有效载荷是如何分离的
    2. 有效载荷如何交付交付的
  • 对于HTTP请求报文来说,我们需要解决以下问题:

    1. HTTP是如何做的读取到完整的报文的
    2. HTTP是如何进行反序列化的
  • 回答问题

    1. HTTP的报头和有效载荷是通过空行进行分离的
    2. HTTP是顶层协议,所以不需要解决向上交付的问题
    3. 首先读取到空行,说明报头已经读取完整,报头中有一个字段为Content-Length,再读取Content-Length字节大小的内容,即可将请求正文的读取完整,结合起来就将整个报文读取完整
    4. 正文部分可以根据正文的类型进行反序列化,根据类型的不同,方式有很多种,这里先不考虑。其他部分则是通过\r\n反序列化即可

3.2.1.1 资源URL路径

资源URL路径就是图片、网页、音频等资源的路径。

在服务器上,通常会使用一个特定的目录,来保存HTTP服务器的所有资源。

就如下图中,我这个简易的HTTP服务器中,就将主页和图片资源都放在了wwwroot这个目录下。web根目录是web服务器存储网站文件的顶级目录,由于HTTP服务器是web服务器的子集,所以我这里的wwwroot目录就可以被当做web根目录。

在这里插入图片描述

当用户使用的网站并没指定URL,则URL为 \ ,此时用户访问的就是web根目录下的默认文件(通常为首页)。

在这里插入图片描述

当用户使用的网站并指定了URL,则请求中的URL就是用户指定的URL。

在这里插入图片描述


3.2.1.2 请求方法(Method)

下面就是HTTP可以使用的方法,但是最常用的还是GET和POST方法。

GET:可以用来获取资源,也可以用来上传数据(参数)。
POST:可以用来上传数据(参数)。

在这里插入图片描述

在一些场景下,我们需要将数据上传给服务器,例如登录、注册、搜索等,我们通过GET/POST再结合HTML(HTML表单)来实现这样的场景。

下面我实现了一个简单的登录页面,当我输入了用户名和密码后,再点击提交,发现我提交的信息作为了参数被拼接到了URL中。

在这里插入图片描述

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录页面</title>
</head>
<body><form action="login.html">用户名:<br><input type="text" name="name" value="chineseperson04"><br>密码:<br><input type="text" name="password"><br><br><input type="submit" value="登录"></form> <a href="http://47.109.128.33:8888/index.html">回到首页</a>
</body>
</html>

在HTML表单中我们还可以设置提交表单时使用的HTTP请求方法。

下面我就设置了HTTP请求方法为GET,当我提交参数后,参数就直接拼接到了URL中了。
在这里插入图片描述

这里我设置HTTP请求方法为POST,再次提交参数后,发现参数并没有拼接到URL中,而是保存到了HTTP的正文部分了。

在这里插入图片描述

当我默认没有设置HTTP请求方法,则HTTP请求方法默认为GET。

在这里插入图片描述

到这里我们就知道GET方法是直接将参数拼接到URL中,而POST方法是将参数保存到正文中。

这时候有的人就会认为POST方法比GET方法安全,实际上两者都不安全,因为HTTP是明文传输,虽然POST方法无法从URL中读取到参数,但是不代表无法被抓包,通过下图我们也可以看出来,所以所以POST方法比GET方法只是私密性好一些,并没有更安全。

在这里插入图片描述


3.2.1.3 Location头字段(重定向相关)

Location头字段:服务器在重定向响应中通过Location头字段提供新URL。

例如状态码中的301就代表着永久重定向,举个例子:搜索引擎可以爬取网页中的URL关键字,关键字又对应着无数个网络连接。当一个用户通过网页访问一个网站时,这个网站由于改变了域名导致老网站过期了,服务器给用户的响应中状态码为301,并且Location字段中保存着新网站的URL,这时候用户会直接跳转到新网站,然后再过一段时间后还可以使搜索引擎中该网站URL更新。

再例如状态码中的302307代表临时重定向,这有什么用呢?在生活中,当我们想使用某一个刷题网站时,服务器检测到用户未登录则会设置状态码为302或307,并设置Location为登录页面的URL,该网站就会跳转到登录页面,当我们登录完毕后,服务器检测到用户登录完毕后,则会设置状态码为302或307,并设置Location为首页页面的URL,然后再次跳转到首页。临时重定向最主要的功能就是让用户跳转到目标网页。


3.2.2 HTTP响应

3.2.2.1 状态码和状态码描述

状态码:表示网页服务器超文本传输协议响应状态的3位数字代码
状态码描述:状态码对应的简短文本说明

在这里插入图片描述

下面是一些常见的状态码和状态码描述:

状态码状态码描述
200 - OK代表一切正常,服务器处理客户端请求,并返回客户端请求的资源
204 - No Content与200基本相同,但是并不返回给客户端任何内容
301 - Moved Permanently永久重定向,请求资源已经不在,需要新的URL重新访问
302 - Found临时重定向,请求资源还在,但是暂时需要使用新的URL重新访问
304 - Not Modified缓存重定向,没有跳转的含义,表示资源还在,重定向已存在的缓存文件,也就是告诉客户端可以继续使用缓存资源
307- Temporary Redirect临时重定向,类似302,但明确要求客户端保持请求方法不变。
400 - Bad Request客户端报文有错误, 但是是笼统的错误
403 - Forbidden客户端报文没有错误,但是服务器禁止访问资源
404 - Not Found客户端请求的资源,在服务器上不存在
500 - Internal Server Error服务器发生错误,是个笼统的错误码
501 - Not Implemented客户端请求的资源,服务器目前还不支持
502 - Bad Gateway服务器运行正常,但访问后端服务器发生错误
503 - Service Unavailable服务器繁忙,无法进行响应

3.2.3 请求与响应共有字段

3.2.3.1 HTTP协议版本

注意:请求与响应中的HTTP协议版本的定义是不同的。

请求中的HTTP协议版本:浏览器支持的HTTP协议
响应中的HTTP协议版本:服务器支持的HTTP协议

请求声明客户端上线,响应声明服务器实现。版本匹配通过协商机制确保兼容性,这样可以让不同的客户端分别有不同的服务。


3.2.3.2 Content-Type

Content-Type在请求与响应中指的都是描述正文内容的类型。

  • 请求中描述的是客户端发送的数据格式
  • 响应中描述的是服务器返回的数据格式

关于Content-Type对应文件后缀,我这里列举一些常见的,其他的大家可以到HTTP content-type这个网站中了解。

Content-Type文件后缀
text/html.html / .htm
application/json.json
image/jpeg.jpg / .jpeg
image/png.png
application/pdf.pdf
text/plain.txt

3.2.4 Cookie 和 Session

3.2.4.1 Cookie

首先我们需要知道HTTP是无连接无状态的,无状态就是HTTP 协议在设计上不会保留客户端与服务器之间的交互状态,这就导致了服务器并不知道后序的请求是否来自同一个用户。

例如:用户想要在某刷题网站上刷题,这时候就需要用户登录,登录完毕后,用户回到了首页,此时用户想要完成第一道题目,这时候服务器不知道这个用户已经登录过了,需要让用户再次登录,当用户完成第一题,需要完成第二道题时,服务器又不认识这个用户了,又需要用户再次登录,多次登录势必会导致用户不想再次使用了。

在这里插入图片描述

如何解决用户需要多次登录的问题呢?这里就要提到Cookie了。

当用户首次登录成功以后,服务器会在响应报头中添加一个或多个SetCookie字段,当浏览器得到响应后,会将Cookie信息保存起来,当用户再次访问该网站时,浏览器会自动将Cookie信息添加到请求报头中,而服务器得到请求报头后,会自动根据请求报头中的Cookie信息进行认证,认证通过则将用户请求的资源发送给浏览器。

在这里插入图片描述

下面我使用Edge浏览器来演示如何看到Cookie,并且删除Cookie后发生什么。

这里我以B站为例,当我将Cookie删除后,当我再次访问B站时,就需要重新登录了。如果在登录状态下,再次访问则不需要登录。

在这里插入图片描述

但是仅仅这样让浏览器保存Cookie可能会导致用户的敏感信息泄露,例如下图,我向我的HTTP服务器发送请求,浏览器中确实保存了Cookie信息,但是很容易的就被抓包了。这样就可能导致别人拿着用户的Cookie信息,登录用户账号,所以Cookie信息不能保存在用户的浏览器中,下面的Session中会讲到。

在这里插入图片描述


3.2.4.2 Session

上面我们提到了浏览器保存Cookie信息会使得用户的敏感信息很容易就被抓包,所以我们就让服务器保存Cookie信息,当用户首次登录成功以后,服务器会创建一个Session(会话)来保存Cookie信息,并且使用一种方法在MySQL中生成一个唯一的Sessionid,将Sessionid添加到响应报头中,浏览器就会只会保存Sessionid,当用户再次访问的时候,浏览器会自动建Sessionid添加到请求报头中,服务器会自动根据Sessionid在服务器中查找并认证,认证通过则将用户请求的资源发送给浏览器。

虽然浏览器中的Sessionid会被抓包,别人也可以使用Sessionid来登录用户的账号,但是却无法抓取用户的敏感信息了。

如何减少别人登录用户的账号呢?大家可以去查一下,可以设置Session的时长,到时间后,Session就自动失效,需要用户再次使用账号密码进行登录,也可以匹配登录账号的地理位置,当位置相隔过大时,就让Session失效,让用户再次使用账号密码进行登录。

一个服务器中一定会有很多用户同时登录,那服务器中就会存在很多的Session(Sessionid),所以服务器一定需要对Session进行管理,管理就需要先描述再组织。(先描述)定义一个Session类,将一个用户的Cookie信息保存起来,(再组织)定义一个SessionManager类,使用某种数据结构将所有的Session管理起来,SessionManager中需要包含对Session的增删查改的方法。

在这里插入图片描述


四、实现一个简单的HTTP服务器

整体HTTP服务器文件结构
在这里插入图片描述

4.1 Socket.hpp(封装套接字)

#pragma once#include <iostream>
#include <string>
#include <string.h>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <errno.h>#define CONV(addrptr) (struct sockaddr*)addrptrenum{Socket_err = 1,Bind_err,Listen_err
};const static int defalutsockfd = -1;
const int defalutbacklog = 5;class Socket
{
public:virtual ~Socket(){};virtual void CreateSocketOrDie() = 0;virtual void BindSocketOrDie(uint16_t port) = 0;virtual void ListenSocketOrDie(int backlog) = 0;virtual Socket* AcceptConnection(std::string* ip , uint16_t* port) = 0;virtual bool ConnectServer(const std::string& serverip , uint16_t serverport) = 0;virtual int GetSockFd() = 0;virtual void SetSockFd(int sockfd) = 0;virtual void CloseSockFd() = 0;virtual bool Recv(std::string& buffer,int size) = 0;virtual void Send(const std::string& send_string) = 0;virtual void ReUseAddr() = 0;public:void BuildListenSocketMethod(uint16_t port){CreateSocketOrDie();ReUseAddr();BindSocketOrDie(port);ListenSocketOrDie(defalutbacklog);}bool BuildConnectSocketMethod(const std::string& serverip , uint16_t serverport){CreateSocketOrDie();return ConnectServer(serverip,serverport);}void BuildNormalSocketMethod(int sockfd){SetSockFd(sockfd);}
};class TcpSocket : public Socket
{
public:TcpSocket(int sockfd = defalutsockfd):_sockfd(sockfd){}~TcpSocket(){};void CreateSocketOrDie() override{_sockfd = ::socket(AF_INET,SOCK_STREAM,0);if(_sockfd < 0) exit(Socket_err);}void BindSocketOrDie(uint16_t port) override{struct sockaddr_in addr;memset(&addr,0,sizeof(addr));addr.sin_family = AF_INET;addr.sin_addr.s_addr = INADDR_ANY;addr.sin_port = htons(port);socklen_t len = sizeof(addr);int n = ::bind(_sockfd,CONV(&addr),len);if(n < 0) exit(Bind_err);}void ListenSocketOrDie(int backlog) override{int n = ::listen(_sockfd,backlog);if(n < 0) exit(Listen_err);}Socket* AcceptConnection(std::string* clientip , uint16_t* clientport) override{struct sockaddr_in client;memset(&client,0,sizeof(client));socklen_t len = sizeof(client);int fd = ::accept(_sockfd,CONV(&client),&len);if(fd < 0) return nullptr;char buffer[64];inet_ntop(AF_INET,&client.sin_addr,buffer,len);*clientip = buffer;*clientport = ntohs(client.sin_port);Socket* s = new TcpSocket(fd);return s;}   bool ConnectServer(const std::string& serverip , uint16_t serverport) override{struct sockaddr_in server;memset(&server,0,sizeof(server));server.sin_family = AF_INET;// server.sin_addr.s_addr =  inet_addr(serverip.c_str());inet_pton(AF_INET,serverip.c_str(),&server.sin_addr);server.sin_port = htons(serverport);socklen_t len = sizeof(server);int n = connect(_sockfd,CONV(&server),len);if(n < 0) return false;else return true;}int GetSockFd() override{return _sockfd;}void SetSockFd(int sockfd) override{_sockfd = sockfd;}void CloseSockFd() override{if(_sockfd > defalutsockfd){close(_sockfd);}}bool Recv(std::string& buffer , int size) override{char inbuffer[size];int n = recv(_sockfd,inbuffer,sizeof(inbuffer)-1,0);if(n > 0){inbuffer[n] = 0;}else{return false;}buffer += inbuffer;return true;}void Send(const std::string& send_string) override{send(_sockfd,send_string.c_str(),send_string.size(),0);}virtual void ReUseAddr() override{int opt = 1;setsockopt(_sockfd,SOL_SOCKET,SO_REUSEADDR,&opt,sizeof(opt));}private:int _sockfd;
};

4.2 HttpProtocol.hpp(自定义HTTP协议)

#pragma once
#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <fstream>const std::string BlankSep = "\r\n"; // 空行
const std::string SpaceSep = " "; // 空格const std::string wwwroot = "./wwwroot";
const std::string homepage = "index.html";class HttpRequest
{
public:HttpRequest(): _request_blank(BlankSep), _path(wwwroot) {};bool Getline(std::string &request, std::string &line){auto pos = request.find(BlankSep);if (pos == request.size())return false;line = request.substr(0, pos);request.erase(0, pos + BlankSep.size());return true;}// 反序列化void Deserialize(std::string &request){std::string line;bool flag = Getline(request, line);_request_line = line;while (true){flag = Getline(request, line);if (flag && line.empty()){_request_content = request;break;}else if (flag && !line.empty()){_request_hander.push_back(line);}else{break;}}}// 将请求行进行拆解void ParseRequestLine(){std::stringstream in(_request_line);in >> _method >> _url >> _version;if (_url == "/")_path += ("/" + homepage);else_path += _url;}void ParseSuffix(){int pos = _request_line.rfind(".");if (pos != std::string::npos)_suffix = _request_line.substr(pos);}void Parse(){// 将请求行每个部分拆出来ParseRequestLine();// 获取请求资源的后缀ParseSuffix();}void Debug(){std::cout << "_request_line:" << _request_line << std::endl;for (auto line : _request_hander){std::cout << line << std::endl;}std::cout << _request_blank << std::endl;std::cout << "_request_content:" << _request_content << std::endl;std::cout << "method : " << _method << "  url : " << _url << "  version : " << _version << std::endl;}std::string GetFileContentHelper(const std::string &path){std::ifstream in(path);if (!in.is_open())return "";in.seekg(0, in.end);int filesize = in.tellg();in.seekg(0, in.beg);std::string content;content.resize(filesize);in.read((char *)content.c_str(), filesize);in.close();return content; }std::string GetFileContent(){return GetFileContentHelper(_path);}std::string Get_404(){return GetFileContentHelper(wwwroot + "/" + "404.html");}std::string Url(){return _url;}std::string Path(){return _path;}std::string Suffix(){return _suffix;}~HttpRequest() {};private:std::string _request_line;                // 请求行std::vector<std::string> _request_hander; // 请求报头std::string _request_blank;               // 空白行std::string _request_content;             // 请求内容std::string _method;std::string _url;std::string _version;std::string _path;std::string _suffix;
};class HttpResponse
{
public:HttpResponse():_response_blank(BlankSep),_method("HTTP 1.0"),_code(200),_desc("OK"){};void SetCode(int code){_code = code;}void SetDesc(const std::string& desc){_desc = desc;}void MakeResponseLine(){_response_line = _method + SpaceSep + std::to_string(_code) + SpaceSep + _desc + BlankSep;}void AddHander(const std::string line){_response_hander.push_back(line);}void AddContent(const std::string& content){_response_content = content;}std::string Serialize(){// 响应行std::string response_str = _response_line;// 响应报头for(auto& line : _response_hander){response_str += line;}// 空行response_str += _response_blank;// 响应正文response_str += _response_content;return response_str;}~HttpResponse(){};private:std::string _response_line;std::vector<std::string> _response_hander;std::string _response_blank;std::string _response_content;std::string _method;int _code;std::string _desc;
};

4.3 TcpServer.hpp(服务端封装)

#pragma once#include "Socket.hpp"#include <string>
#include <functional>
#include <pthread.h>using func_t = std::function<std::string(std::string &)>;class TcpServer;class ThreadDate
{
public:ThreadDate(TcpServer *tser_this, Socket *socket): _this(tser_this), _socket(socket){}public:TcpServer *_this;Socket *_socket;
};class TcpServer
{
public:TcpServer(uint16_t port, func_t handler_request): _port(port), _listensock(new TcpSocket()), _handler_request(handler_request){_listensock->BuildListenSocketMethod(_port);}static void *HandlerRequest(void *arg){pthread_detach(pthread_self());ThreadDate *th = (ThreadDate *)arg;std::string inbufferstream;// 1、读取报文if (th->_socket->Recv(inbufferstream, 4096)){// 2、调用函数处理报文std::string send_string = th->_this->_handler_request(inbufferstream);// 3、发送th->_socket->Send(send_string);}th->_socket->CloseSockFd();delete th->_socket;delete th;return nullptr;}void Loop(){while (1){std::string clientip;uint16_t clientport;// 接收连接会返回一个新的文件描述符Socket *NewSocket = _listensock->AcceptConnection(&clientip, &clientport);std::cout << "get a new sockfd , sockfd : " << NewSocket->GetSockFd()<< " , clinet info " << clientip << ":" << clientport << std::endl;// 创建线程pthread_t pid;ThreadDate *td = new ThreadDate(this, NewSocket);pthread_create(&pid, nullptr, HandlerRequest, td);}_listensock->CloseSockFd();}~TcpServer(){}private:TcpSocket *_listensock; // 监听套接字uint16_t _port;public:func_t _handler_request; // 回调函数
};

4.4 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><h2>欢迎来到是阿建吖的主页!</h2><a href="http://47.109.128.33:8888/login.html">登录</a></body>
</html>

4.5 wwwroot/login.html(登录页面)

<!DOCTYPE html>
<html lang="en">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>登录页面</title>
</head>
<body><form action="login.html" method="post">用户名:<br><input type="text" name="name" value="chineseperson04"><br>密码:<br><input type="text" name="password"><br><br><input type="submit" value="登录"></form> <a href="http://47.109.128.33:8888/index.html">回到首页</a>
</body>
</html>

4.6 wwwroot/404.html(404页面、网上搜)

<!DOCTYPE html>
<html lang="zh-CN">
<head><meta charset="UTF-8"><meta name="viewport" content="width=device-width, initial-scale=1.0"><title>404 - 页面未找到</title><style>body {margin: 0;padding: 0;font-family: 'Arial', sans-serif;background: linear-gradient(135deg, #ff7e5f, #feb47b);color: #fff;display: flex;justify-content: center;align-items: center;height: 100vh;overflow: hidden;}.container {text-align: center;animation: fadeIn 2s ease-in-out;}h1 {font-size: 10rem;margin: 0;animation: float 3s ease-in-out infinite;}p {font-size: 1.5rem;margin-top: -20px;}a {display: inline-block;margin-top: 20px;padding: 10px 20px;font-size: 1rem;color: #fff;background: rgba(255, 255, 255, 0.2);text-decoration: none;border-radius: 25px;transition: all 0.3s ease;}a:hover {background: rgba(255, 255, 255, 0.4);transform: scale(1.1);}@keyframes float {0%, 100% {transform: translateY(0);}50% {transform: translateY(-20px);}}@keyframes fadeIn {from {opacity: 0;transform: scale(0.8);}to {opacity: 1;transform: scale(1);}}</style>
</head>
<body><div class="container"><h1>404</h1><p>哎呀!页面走丢了...</p><a href="/">返回首页</a></div>
</body>
</html>

4.7 Main.cpp(服务端)

#include "TcpServer.hpp"
#include "HttpProtocol.hpp"#include <iostream>
#include <unistd.h>
#include <string>
#include <string.h>
#include <memory>
#include <fstream>using namespace std;string CodeToType(int code)
{switch (code){case 200:return "OK";break;case 204:return "No Content";break;case 301:return "Moved Permanently";break;case 302:return "Found ";break;case 304:return "Not Modified";break;case 307:return "Temporary Redirect";break;case 400:return "Bad Request";break;case 403:return "Forbidden ";break;case 404:return "Not Found";break;case 500:return "Internal Server Error";break;case 501:return "Not Implemented";break;case 502:return "Bad Gateway";break;case 503:return "Service Unavailable";break;default:return "Unknown";break;}
}string SuffixToType(const string &suffix)
{if (suffix == ".html" || suffix == ".htm")return "text/html";else if (suffix == ".jpg" || suffix == ".jpeg")return "image/jpeg";else if (suffix == ".png")return "image/png";elsereturn "";
}string handler_http_request(string &request)
{HttpRequest req;int code = 200;string desc = "OK";req.Deserialize(request);req.Parse();req.Debug();std::string content = req.GetFileContent();if (content.empty()){code = 404;desc = CodeToType(404);content = req.Get_404();}HttpResponse resp;resp.SetCode(code);resp.SetDesc(desc);resp.MakeResponseLine();std::string http_content_length = "Content-Length: " + std::to_string(content.size()) + "\r\n";resp.AddHander(http_content_length);std::string http_content_type = "Content-Type: " + SuffixToType(req.Suffix()) + "\r\n";resp.AddHander(http_content_type);resp.AddContent(content);string response_str = resp.Serialize();return response_str;
}void Usage(string proc)
{cout << proc << " serverport" << endl;
}int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(1);}uint16_t serverport = stoi(argv[1]);unique_ptr<TcpServer> up = make_unique<TcpServer>(serverport, handler_http_request);up->Loop();return 0;
}

结尾

如果有什么建议和疑问,或是有什么错误,大家可以在评论区中提出。
希望大家以后也能和我一起进步!!🌹🌹
如果这篇文章对你有用的话,希望大家给一个三连支持一下!!🌹🌹

在这里插入图片描述

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/web/88789.shtml
繁体地址,请注明出处:http://hk.pswp.cn/web/88789.shtml
英文地址,请注明出处:http://en.pswp.cn/web/88789.shtml

如若内容造成侵权/违法违规/事实不符,请联系英文站点网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

编程实践:单例模式(懒汉模式+饿汉模式)

说明:本专栏文章有两种解锁方案 1:付费订阅,畅享所有文章 2:免费获取,点击下方链接,关注,自动获取免费链接 https://free-img.400040.xyz/4/2025/04/29/6810a50b7ac8b.jpg 主题:C++ 单例模式 什么是单例模式

破局电机制造四大痛点:MES与AI视觉的协同智造实践

万界星空科技电机行业MES系统解决方案是针对电机制造过程中多工序协同难、质量追溯复杂、设备管理要求高等痛点设计的数字化管理系统。一、电机行业的核心痛点1. 多工序协同困难 电机制造涉及绕线、装配、测试等多道工序&#xff0c;工艺衔接复杂&#xff0c;传统人工调度效率…

HTML 初体验

HTML&#xff08;超文本标记语言&#xff09;全称&#xff1a;HyperText Markup Language。超文本是什么&#xff1f;答&#xff1a;超文本就是网页中的链接。标记是什么&#xff1f;答&#xff1a;标记也叫标签&#xff0c;是带尖括号的文本。需求1&#xff1a;将“我爱中国”…

网络层TCP机制

1.确认应答机制由于发送信息的距离可能较远,可能出现后发的信息先到的情况,怎么办?TCP将每个字节的数据都进行了编号,即为序列号如何分辨一个数据包是普通数据还是应答数据呢2.超时重传由于丢包是一个随机的事件,因此在上述tcp传输的过程中,丢包就存在两种情况但是在发送方的角…

【一起来学AI大模型】微调技术:LoRA(Low-Rank Adaptation) 的实战应用

LoRA&#xff08;Low-Rank Adaptation&#xff09; 的实战应用&#xff0c;使用 Hugging Face 的 peft (Parameter-Efficient Fine-Tuning) 库对大型语言模型进行高效微调。LoRA 因其显著降低资源消耗&#xff08;显存和计算&#xff09;同时保持接近全量微调性能的特点&#x…

RedisJSON 内存占用剖析与调优

一、基础内存模型指针包装 所有 JSON 值&#xff08;标量、对象、数组、字符串等&#xff09;至少占用 8 字节&#xff0c;用于存储一个带类型标记的指针。标量与空容器 null、true、false、小整数&#xff08;静态缓存&#xff09;、空字符串、空数组、空对象 均不分配额外内存…

【LeetCode 热题 100】23. 合并 K 个升序链表——(解法一)逐一合并

Problem: 23. 合并 K 个升序链表 题目&#xff1a;给你一个链表数组&#xff0c;每个链表都已经按升序排列。 请你将所有链表合并到一个升序链表中&#xff0c;返回合并后的链表。 文章目录整体思路完整代码时空复杂度时间复杂度&#xff1a;O(K * N)空间复杂度&#xff1a;O(1…

垃圾收集器-Serial Old

第一章 引言1.1 JVM 中垃圾收集的简要概述JVM&#xff08;Java Virtual Machine&#xff09;作为 Java 程序的运行时环境&#xff0c;负责将字节码加载至内存并执行&#xff0c;同时也承担着内存管理的重任。垃圾收集&#xff08;Garbage Collection&#xff0c;简称 GC&#x…

Docker(02) Docker-Compose、Dockerfile镜像构建、Portainer

Docker-Compose 1、Docker Desktop 在Windows上安装Docker服务&#xff0c;可以使用Docker Desktop这个应用程序。 下载并安装这样的一个安装包 安装好后&#xff1a;执行命令 docker --version 从Docker Hub提取hello-world映像并运行一个容器&#xff1a; docker run h…

大数据时代UI前端的用户体验设计新思维:以数据为驱动的情感化设计

hello宝子们...我们是艾斯视觉擅长ui设计和前端数字孪生、大数据、三维建模、三维动画10年经验!希望我的分享能帮助到您!如需帮助可以评论关注私信我们一起探讨!致敬感谢感恩!一、引言&#xff1a;从 “经验设计” 到 “数据共情” 的体验革命传统 UI 设计常陷入 “设计师主观经…

TypeScript 学习手册

1.TypeScript 概念 TypeScript&#xff08;简称 TS&#xff0c;静态类型&#xff09;是微软公司开发的一种基于 JavaScript &#xff08;简称 JS&#xff0c;动态类型&#xff09;语言的编程语言。TypeScript 可以看成是 JavaScript 的超集&#xff08;superset&#xff09;&a…

掌握现代CSS:变量、变形函数与动态计算

CSS近年来发展迅速&#xff0c;引入了许多强大的功能&#xff0c;如变量、高级变形函数和动态计算能力。本文将深入探讨如何在CSS中设置并使用变量&#xff0c;以及如何有效利用translate3d、translateY和translateX等变形方法。我们还将解析var()和calc()函数的关键作用。一、…

贝尔量子实验设想漏洞

1 0 1 0 1 1 0 1 0 1 1 1 0 0 1 0 带墨镜如果先上下交换再左右交换&#xff0c;很可能不一样的概率是2%&#xff0c;但是因为交换诞生了一个与之前序列相同的所以不一样概率变成1%&#xff0c;我们在测的时候不能这么测啊&#xff0c;你得看序列完…

在 Android 库模块(AAR)中,BuildConfig 默认不会自动生成 VERSION_CODE 和 VERSION_NAME 字段

为什么AAR库模块的 BuildConfig 没有 versionCode 和 versionName&#xff1f; aar库模块的 BuildConfig 默认不包含版本信息 应用模块&#xff08;com.android.application&#xff09;会自动生成 versionCode 和 versionName 到 BuildConfig。但库模块&#xff08;com.androi…

强化学习 (11)随机近似

计算均值的新方法有两种方法。第一种方法很直接&#xff0c;即收集所有样本后计算平均值&#xff1b;但这种方法的缺点是&#xff0c;若样本是在一段时间内逐个收集的&#xff0c;我们必须等到所有样本都收集完毕。第二种方法可避免此缺点&#xff0c;因为它以增量迭代的方式计…

PHP `implode` 深度解析:从基础到高阶实战指南

文章目录一、基础语法与底层原理执行过程解析&#xff1a;二、性能关键&#xff1a;避免隐含陷阱1. 类型转换黑盒2. 大数组内存优化3. 关联数组处理三、高阶应用场景1. SQL语句安全构建2. CSV文件生成3. 模板引擎实现四、多维数组处理方案1. 递归降维2. JSON转换桥接五、性能对…

开发语言中关于面向对象和面向过程的笔记

开发语言中关于面向对象和面向过程的笔记市面主流语言分类面向过程面向对象市面主流语言分类 面向过程编程&#xff08;Procedural Programming&#xff09;&#xff1a;C语言&#xff1b;面向对象编程语言&#xff08;Object-Oriented Programming, OOP&#xff09; &#xf…

AI产品经理面试宝典第3天:技术分层、边界与市场全景系列面试题

面试指导 面试官:请从技术实现效果的角度,解释AI技术分层。 你的回答: AI技术分为三层。 第一层是认知层:通过图像处理、语音识别、自然语言理解等技术,让机器感知环境。比如摄像头识别行人动作,麦克风捕捉用户指令。 第二层是预测层:基于行为数据预判下一步需求。例如…

Intel英特尔ICH7R/ICH8R/ICH9R/ICH10R系列下载地址--intel_msm_8961002 下载 Version 8.9.6.1002

Intel英特尔ICH7R/ICH8R/ICH9R/ICH10R系列下载地址intel_msm_8961002 下载 Version 8.9.6.1002https://xiazai.zol.com.cn/detail/66/653449.shtml通过网盘分享的文件&#xff1a;intel_msm_8961002.zip 链接: https://pan.baidu.com/s/13N9ZLXWkaWaEHQ5P90Jt0g?pwd3790 提取码…

AI(学习笔记第五课) 使用langchain进行AI开发 load documents(web)

文章目录AI(学习笔记第五课) 使用langchain进行AI开发 load documents(web)学习内容&#xff1a;1.load documents&#xff08;web&#xff09;1.1 学习url1.2 提前安装python的package1.2 使用WebBaseLoader进行webpage的load1.3 使用BeautifulSoup4进行webpage的部分截取1.4 …