【Linux | 網絡】應用層(HTTP)

在這里插入圖片描述

目錄

  • 一、認識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 …