【Linux網絡】構建與優化HTTP請求處理 - HttpRequest從理解到實現

📢博客主頁:https://blog.csdn.net/2301_779549673
📢博客倉庫:https://gitee.com/JohnKingW/linux_test/tree/master/lesson
📢歡迎點贊 👍 收藏 ?留言 📝 如有錯誤敬請指正!
📢本文由 JohnKi 原創,首發于 CSDN🙉
📢未來很長,值得我們全力奔赴更美好的生活?

在這里插入圖片描述

在這里插入圖片描述

文章目錄

  • 🏳??🌈一、HttpRequest 類
    • 1.1 基本結構
    • 1.2 構造函數、析構函數
    • 1.3 反序列化函數 Descrialize
    • 1.4 獲取一行字符串 GetLine()
    • 1.5 打印方法 Print
    • 1.6 解析請求行 PraseReqLine
    • 1.7 解析請求頭 void PraseHeader();
    • 1.8 增加路徑字段
    • 1.9 測試
  • 🏳??🌈二 、整體代碼
  • 👥總結


🏳??🌈一、HttpRequest 類

1.1 基本結構

我們結合這張圖,構建出 http請求 的基本結構,并定義一些基本方法
在這里插入圖片描述

const static std::string _base_sep = "\r\n";     // static 關鍵字使變量具有內部鏈接,僅當前翻譯單元(源文件)可見。class HttpRequest {
private:std::string GetLine(std::string& reqstr); // 獲取一行信息void PraseReqLine();                      // 解析請求行void PraseHeader();                       // 解析請求頭
public:HttpRequest();void Descrialize(std::string& reqstr);void Print();~HttpRequest();private:std::string _req_line;                 // 請求行std::vector<std::string> _req_headers; // 請求報頭std::string _blank_line;               // 空行std::string _req_body;                 // 請求體
};

1.2 構造函數、析構函數

構造函數初始化空行即可,因為空行是固定的,析構函數無需處理!

HttpRequest() : _blank_line(_base_sep) {}
~HttpRequest() {}

1.3 反序列化函數 Descrialize

我們這里是需要解析獲取到的請求,所以用的方法自然是 反序列化

void Descrialize(std::string& reqstr) {// 基本的反序列化_req_line = GetLine(reqstr); // 讀取第一行請求行// 請求報頭std::string header;do {header = GetLine(reqstr);// 如果既不是空,也不是空行,就是請求報頭,加入到請求報頭列表中if (header.empty())break;else if (header == _base_sep)break;_req_headers.push_back(header);} while (true);// 正文if (!reqstr.empty())_req_body = reqstr;
}

1.4 獲取一行字符串 GetLine()

// 獲取一行信息
std::string GetLine(std::string& reqstr) {auto pos = reqstr.find(_base_sep);if (pos == std::string::npos)return "";std::string line = reqstr.substr(0, pos);  // 截取一行有效信息reqstr.erase(0, pos + _base_sep.length()); // 刪除有效信息和分隔符return line.empty() ? _base_sep: line; // 有效信息為空則返回分隔符,否則返回有效信息
}

1.5 打印方法 Print

void Print() {std::cout << "----------------------------------------" << std::endl;std::cout << "請求行: " << _req_line << std::endl;std::cout << "請求報頭: " << std::endl;for (auto& header : _req_headers) {std::cout << header << std::endl;}std::cout << "空行: " << _blank_line << std::endl;std::cout << "請求體: " << _req_body << std::endl;
}

當我們使用瀏覽器訪問我們的服務器,就能夠成功地將我們需要地所有信息給序列化出來,這里沒有請求,所以請求體為空

在這里插入圖片描述
在這里插入圖片描述

1.6 解析請求行 PraseReqLine

我們已經知道請求行的組成如下,所以我們可以進一步細分 HttpRequest 類,增加響應的請求行的成員變量
在這里插入圖片描述

std::string _method;  // 請求方法
std::string _url;     // 請求url
std::string _version; // 請求版本

將 _req_line(請求行)封裝為字符串流,按空格分隔讀取方法、路徑、協議版本

// 解析請求行
void PraseReqLine() {// 以空格為分隔符,不斷讀取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;
}

1.7 解析請求頭 void PraseHeader();

在這里插入圖片描述
我們可以知道請求報頭中存在類似哈希表的 KV 結構,因此我們可以是使用一個 unordered_map,存儲每一個鍵值對

在這里插入圖片描述

根據我們之前獲取到的請求報頭,可以知道分隔符是 ": ",可以根據這個進行解析請求報頭

// 解析請求頭
void PraseHeader() {for (auto& header : _req_headers) {auto pos = header.find(':');if (pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + _line_sep.size());if (k.empty() || v.empty())continue;_headers_kv[k] = v;}
}

1.8 增加路徑字段

**我們向服務器請求的時候,需要知道資源的路徑,因此我們可以增加路徑字段
**

  • 我們提供一個路徑的前綴 wwwroot
  • 并且當這個用戶訪問的路徑為 / 時,提供默認路徑 default.html
const static std::string _prefix_path = "wwwroot";          // 默認前綴路勁
const static std::string _default_path = "default.html";    // 默認路徑

我們可以在構造函數時,給路勁添加上默認前綴路徑

HttpRequest() : _blank_line(_base_sep), _path(_prefix_path) {}

我們在解析 請求行url 進行分析,判斷是否為空,并且給 path 賦值

void PraseReqLine(){    // 以空格為分隔符,不斷讀取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;     _path += _url;// 處理url,如果是根目錄,則返回默認路徑if(_url == "/")_path += _default_path;}

提供兩個新方法用來獲取當前的 url路徑

std::string Url() {LOG(LogLevel::INFO) << "client want url : " << _url;return _url;
}
std::string Path() {LOG(LogLevel::INFO) << "client want url : " << _path;return _path;
}

1.9 測試

我們運行服務端后,再用瀏覽器訪問我們的服務器,成功捕捉到了兩次請求,

  • ?第一次請求?(端口 3362):
    瀏覽器主動請求你輸入的 URL(如 http://119.91.133.45:8080/),服務端返回頁面/default.html
  • 第二次請求?(端口 3361):
    瀏覽器 ?自動請求網站圖標? /favicon.ico,用于在標簽頁、書簽欄顯示小圖標。若服務端未顯式處理該請求,瀏覽器仍會嘗試獲取。
    在這里插入圖片描述

🏳??🌈二 、整體代碼

#pragma once#include <iostream>
#include <string>
#include <vector>
#include <sstream>
#include <unordered_map>
#include "Log.hpp"using namespace LogModule;const static std::string _base_sep = "\r\n";     // static 關鍵字使變量具有內部鏈接,僅當前翻譯單元(源文件)可見。
// const static std::string _base_sep = "\r\n";  // 默認具有外部鏈接,其他文件可通過 extern 引用。
const static std::string _line_sep = ": ";
const static std::string _prefix_path = "/wwwroot";          // 默認前綴路勁
const static std::string _default_path = "default.html";    // 默認路徑namespace HttpServer{class HttpRequest{private:// 獲取一行信息std::string GetLine(std::string& reqstr){auto pos = reqstr.find(_base_sep);if(pos == std::string::npos) return "";std::string line = reqstr.substr(0, pos);       // 截取一行有效信息reqstr.erase(0, pos + _base_sep.length());      // 刪除有效信息和分隔符return line.empty() ? _base_sep : line;         // 有效信息為空則返回分隔符,否則返回有效信息}// 解析請求行void PraseReqLine(){    // 以空格為分隔符,不斷讀取std::stringstream ss(_req_line);ss >> _method >> _url >> _version;     _path += _url;// 處理url,如果是根目錄,則返回默認路徑if(_url == "/")_path = _default_path;}// 解析請求頭void PraseHeader(){for(auto& header : _req_headers){auto pos = header.find(':');if(pos == std::string::npos)continue;std::string k = header.substr(0, pos);std::string v = header.substr(pos + _line_sep.size());if(k.empty() || v.empty()) continue;_headers_kv[k] = v;}}public:HttpRequest() : _blank_line(_base_sep), _path(_prefix_path) {}void Descrialize(std::string& reqstr){// 基本的反序列化_req_line = GetLine(reqstr);    // 讀取第一行請求行// 請求報頭std::string header;do{header = GetLine(reqstr);// 如果既不是空,也不是空行,就是請求報頭,加入到請求報頭列表中if(header.empty()) break;else if(header == _base_sep) break;_req_headers.push_back(header);}while(true);// 正文if(!reqstr.empty())_req_body = reqstr;// 進一步反序列化請求行PraseReqLine();// 分割請求報頭,獲取鍵值對PraseHeader(); }void Print(){std::cout << "----------------------------------------" <<std::endl;std::cout << "請求行: ###" << _req_line << std::endl;std::cout << "請求報頭: " << std::endl;for(auto& header : _req_headers){std::cout << "@@@" << header << std::endl;}std::cout << "空行: " << _blank_line << std::endl;std::cout << "請求體: " << _req_body << std::endl;std::cout << "Method: " << _method << std::endl;std::cout << "Url: " << _url << std::endl;std::cout << "Version: " << _version << std::endl;}std::string Url(){LOG(LogLevel::INFO) << "client want url : " << _url;  return _url;}std::string Path(){LOG(LogLevel::INFO) << "client want path : " << _path;  return _path;}~HttpRequest() {}private:std::string _req_line;                      // 請求行std::vector<std::string> _req_headers;      // 請求報頭std::string _blank_line;                    // 空行std::string _req_body;                      // 請求體std::string _method;                         // 請求方法std::string _path;   // 資源路徑std::string _url;                            // 請求urlstd::string _version;                        // 請求版本std::unordered_map<std::string, std::string> _headers_kv; // 存儲每行報文的哈希表};class HttpHandler{public:HttpHandler(){}std::string handle(std::string req){std::cout << "------------------------------------" << std::endl;std::cout << req;HttpRequest req_obj;req_obj.Descrialize(req);// req_obj.Print();std::string path = req_obj.Path();std::string url = req_obj.Url();std::string responsestr = "HTTP/1.1 200 OK\r\n";responsestr += "Content-Type: text/html\r\n";responsestr += "\r\n";responsestr += "<html><h1>hello linux,hello net!<h2></html>";return responsestr;}~HttpHandler(){}};
}

👥總結

本篇博文對 【Linux網絡】構建與優化HTTP請求處理 - 從HttpRequest到HttpServer 做了一個較為詳細的介紹,不知道對你有沒有幫助呢

覺得博主寫得還不錯的三連支持下吧!會繼續努力的~

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/diannao/80615.shtml
繁體地址,請注明出處:http://hk.pswp.cn/diannao/80615.shtml
英文地址,請注明出處:http://en.pswp.cn/diannao/80615.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

Day12(回溯法)——LeetCode51.N皇后39.組合總和

1 前言 今天刷了三道回溯法和一道每日推薦&#xff0c;三道回溯法也迷迷糊糊的&#xff0c;每日推薦把自己繞進去了&#xff0c;雖然是一道之前做過的題的變種。刷的腦子疼。。。今天挑兩道回溯題寫一下吧&#xff0c;其中有一道是之前做過的N皇后&#xff0c;今天在詳細寫一寫…

初階數據結構:二叉搜索樹

目錄 概念 性能 效率分析 二分缺陷 功能 插入 查找 刪除 實現 應用 概念 二叉搜索樹&#xff08;又稱&#xff1a;二叉排序樹&#xff09;&#xff0c;是由一些具有特別性質的二叉樹衍變而來。 只要一棵二叉樹具備以下性質&#xff0c;即可稱作二叉搜索樹。 【1】若…

詳解springcloud gateway工作原理、斷言、filter、uri、id、全局跨域、globalfilter等以及關鍵源碼實現

1.gateway概念 網關就是當前微服務項目的"統一入口"程序中的網關就是當前微服務項目對外界開放的統一入口所有外界的請求都需要先經過網關才能訪問到我們的程序提供了統一入口之后,方便對所有請求進行統一的檢查和管理 2. 網關的主要功能 將所有請求統一經過網關網…

C#中的弱引用使用

弱引用&#xff08;Weak Reference&#xff09;是一種特殊的引用類型&#xff0c;它允許你引用一個對象&#xff0c;但不會阻止該對象被垃圾回收器&#xff08;GC&#xff09;回收。弱引用通常用于需要緩存或跟蹤對象&#xff0c;但又不希望因保留引用而導致內存泄漏的場景。弱…

spring響應式編程系列:異步生產數據

目錄 示例 大致流程 create new MonoCreate subscribe new LambdaMonoSubscriber monoCreate.subscribe accept success onNext 時序圖 類圖 數據發布者 MonoCreate 數據訂閱者 LambdaMonoSubscriber 訂閱的消息體 DefaultMonoSink 本篇文章我們來研究如何將…

MCP Python SDK構建的**SQLite瀏覽器**的完整操作指南

以下是使用MCP Python SDK構建的SQLite瀏覽器的完整操作指南&#xff1a; 一、環境準備 安裝依賴 # 安裝MCP SDK及SQLite支持 pip install mcp sqlite3創建測試數據庫 sqlite3 test.db <<EOF CREATE TABLE users (id INTEGER PRIMARY KEY, name TEXT, email TEXT); IN…

【Python爬蟲基礎篇】--3.cookie和session

目錄 1.cookie 1.1.定義 1.2.參數 1.3.分類 2.session 3.使用cookie登錄微博 4.使用session登錄 1.cookie 由于http是一個無狀態的協議&#xff0c;請求與請求之間無法相互傳遞或者記錄一些信息&#xff0c;cookie和session正是為了解決這個問題而產生。 例子&#xff1…

風車郵箱系統詳細使用指南:Windows與Ubuntu雙平臺解析

風車郵箱系統V1.2使用手冊 風車郵箱系統詳細使用指南&#xff1a;Windows與Ubuntu雙平臺解析 前言 在日常網絡活動中&#xff0c;我們經常需要一個臨時郵箱來注冊各類網站或接收驗證碼&#xff0c;但不想使用自己的真實郵箱。「風車無線郵箱系統」作為一款優秀的臨時郵箱工具…

同樣的接口用postman/apifox能跑通,用jmeter跑就報錯500

之前沒用過jmeter,第一次用調試壓測腳本遇到了問題 一樣的接口用postman能跑通&#xff0c;用jmeter跑就報錯500&#xff0c;百度很多文章都說是該接口需要加一個‘內容編碼’改成utf-8,我加了還是不行 后來我就想到apifox好像有隱藏的header&#xff0c;然后開始比較apifox的…

1656打印路徑-Floyd回溯/圖論-鏈表/數據結構

藍橋賬戶中心 1.稅收&#xff1a; “城市的稅收”&#xff1a;所以是中介點的稅收&#xff0c;經過該點后加上 2.路徑&#xff1a; 用數組存儲前驅節點從而串成鏈表 pre[ i ][ j ]代表的是從 i 到 j 的最短路徑上 j 的前驅節點是什么 那么便可以pre[ i ][ j ]k 把k加入pa…

Eigen矩陣操作類 (Map, Block, 視圖類)

1. Map 類&#xff1a;內存映射&#xff08;零拷貝操作&#xff09; 核心功能 將現有的 C/C 數組或緩沖區映射為 Eigen 矩陣/向量&#xff0c;不復制數據&#xff0c;直接操作原內存。 模板參數 cpp Map<Matrix<Scalar, Rows, Cols, Options, MaxRows, MaxCols>&…

多系統安裝經驗,移動硬盤,ubuntu grub修改/etc/fstab 移動硬盤需要改成nfts格式才能放steam游戲

總結&#xff1a;我硬盤會自動掛載&#xff0c;直接格式化nfts&#xff0c;steam就能裝里面了 機械硬盤裝系統真的不行&#xff0c;超級慢游戲還跑不了 --------------------------------------------------------------------底下都不用看 筆記本一個系統&#xff0c;移動硬盤…

JFLAP SOFTWARE 編譯原理用(自動機繪圖)

csdn全是蛆蟲&#xff0c;2mb的軟件&#xff0c;都在那里搞收費&#xff0c;我就看不慣&#xff0c;我就放出來&#xff0c;那咋了&#xff01;&#xff01;&#xff01; https://pan.baidu.com/s/1IuEfHScynjCCUF5ScF26KA 通過網盤分享的文件&#xff1a;JFLAP7.1.jar 鏈接: h…

[Windows] Disk Sorter文件分類管理軟件 v16.7.18

[Windows] Disk Sorter文件分類管理 鏈接&#xff1a;https://pan.xunlei.com/s/VOOl0sDntAdHvlMkc7N0ZOD-A1?pwd966n# Disk Sorter是一個功能強大的文件分類管理軟件&#xff0c;允許對本地磁盤、網絡共享、NAS設備和企業存儲系統中的文件進行分類&#xff0c;并且支持生成…

STM32提高篇: 藍牙通訊

STM32提高篇: 藍牙通訊 一.藍牙通訊介紹1.藍牙技術類型 二.藍牙協議棧1.藍牙芯片架構2.BLE低功耗藍牙協議棧框架 三.ESP32-C3中的藍牙功能1.廣播2.掃描3.通訊 四.發送和接收 一.藍牙通訊介紹 藍牙&#xff0c;是一種利用低功率無線電&#xff0c;支持設備短距離通信的無線電技…

6.1.多級緩存架構

目錄 一、多級緩存基礎與核心概念 緩存的定義與價值 ? 緩存的應用場景&#xff08;高并發、低延遲、減輕數據庫壓力&#xff09; ? 多級緩存 vs 單級緩存的優劣對比 多級緩存核心組件 ? 本地緩存&#xff08;Caffeine、Guava Cache&#xff09; ? 分布式緩存&#xff08;…

MySQL的MVCC【學習筆記】

MVCC 事務的隔離級別分為四種&#xff0c;其中Read Committed和Repeatable Read隔離級別&#xff0c;部分實現就是通過MVCC&#xff08;Multi-Version Concurrency Control&#xff0c;多版本并發控制&#xff09; 版本鏈 版本鏈是通過undo日志實現的&#xff0c; 事務每次修改…

基于OpenMV+STM32+OLED與YOLOv11+PaddleOCR的嵌入式車牌識別系統開發筆記

基于OpenMV、STM32與OLED的嵌入式車牌識別系統開發筆記 基于OpenMV、STM32與OLED的嵌入式車牌識別系統開發筆記系統架構全景 一、實物演示二、OpenMV端設計要點1. 硬件配置優化2. 智能幀率控制算法3. 數據傳輸協議設計 三、PyTorch后端核心實現&#xff1a;YOLOv11與PaddleOCR的…

C#中常見的設計模式

文章目錄 引言設計模式的分類創建型模式 (Creational Patterns)1. 單例模式 (Singleton)2. 工廠方法模式 (Factory Method)3. 抽象工廠模式 (Abstract Factory)4. 建造者模式 (Builder) 結構型模式 (Structural Patterns)5. 適配器模式 (Adapter)6. 裝飾器模式 (Decorator)7. 外…

Nacos簡介—3.Nacos的配置簡介

大綱 1.Nacos生產集群Web端口與數據庫配置 2.Nacos生產集群的Distro協議核心參數 3.Nacos打通CMDB實現跨機房的就近訪問 4.Nacos基于SPI動態擴展機制來獲取CMDB的數據 5.基于Nacos SPI機制開發CMDB動態擴展 6.Nacos基于CMDB來實現多機房就近訪問 7.Nacos生產集群Prometh…