【Linux】39.一個基礎的HTTP Web服務器

文章目錄

  • 1. 實現一個基礎的HTTP Web服務器
    • 1.1 功能實現:
    • 1.2 Log.hpp-日志記錄器
    • 1.3 HttpServer.hpp-網頁服務器
    • 1.4 Socket.hpp-網絡通信器
    • 1.5 HttpServer.cc-服務器啟動器


1. 實現一個基礎的HTTP Web服務器

1.1 功能實現:

  1. 總體功能

    • 提供Web服務,響應客戶端(瀏覽器)的HTTP請求

    • 支持靜態文件服務(如HTML、圖片等)

    • 多線程處理并發請求

    • 帶日志記錄功能

  1. 具體工作流程
瀏覽器 → 發送HTTP請求 → 服務器↓解析請求↓查找文件↓返回響應↓
瀏覽器 ← 顯示頁面 ← 服務器
  1. 各模塊職責:

日志記錄器(Log.hpp)

  • 記錄服務器運行狀態
  • 錯誤追蹤和調試

網頁服務器(HttpServer.hpp)

  • 解析HTTP請求
  • 處理靜態文件
  • 生成HTTP響應
  • 多線程處理請求

網絡通信器(Socket.hpp)

  • 處理底層網絡通信
  • 管理TCP連接

服務器啟動器(HttpServer.cc)

  • 程序入口
  • 初始化和啟動服務

1.2 Log.hpp-日志記錄器

Log.hpp

#pragma once  // 防止頭文件重復包含// 系統頭文件包含
#include <iostream>     // 標準輸入輸出
#include <time.h>       // 時間相關函數
#include <stdarg.h>     // 可變參數處理
#include <sys/types.h>  // 基本系統數據類型
#include <sys/stat.h>   // 文件狀態
#include <fcntl.h>      // 文件控制選項
#include <unistd.h>     // UNIX標準函數
#include <stdlib.h>     // 標準庫函數// 基礎配置宏定義
#define SIZE 1024      // 緩沖區大小
#define LogFile "log.txt"  // 默認日志文件名// 日志級別定義(按嚴重程度遞增)
#define Info 0      // 普通信息:記錄系統正常操作信息
#define Debug 1     // 調試信息:記錄調試相關信息
#define Warning 2   // 警告信息:記錄潛在問題
#define Error 3     // 錯誤信息:記錄錯誤但不影響系統運行
#define Fatal 4     // 致命錯誤:記錄導致系統崩潰的錯誤// 日志輸出方式定義
#define Screen 1     // 輸出到屏幕:直接顯示在終端
#define Onefile 2    // 輸出到單個文件:所有日志記錄到同一個文件
#define Classfile 3  // 分類輸出:根據日志級別輸出到不同文件class Log {
private:int printMethod;      // 日志輸出方式std::string path;     // 日志文件存儲路徑public:// 構造函數:初始化日志系統Log() {printMethod = Screen;  // 默認輸出到屏幕path = "./log/";       // 默認日志目錄}// 設置日志輸出方式void Enable(int method) {printMethod = method;}// 將日志級別轉換為對應的字符串std::string levelToString(int level) {switch (level) {case Info:    return "Info";case Debug:   return "Debug";case Warning: return "Warning";case Error:   return "Error";case Fatal:   return "Fatal";default:      return "None";}}// 根據設置的輸出方式打印日志void printLog(int level, const std::string &logtxt) {switch (printMethod) {case Screen:    // 輸出到屏幕std::cout << logtxt << std::endl;break;case Onefile:   // 輸出到單個文件printOneFile(LogFile, logtxt);break;case Classfile: // 根據日志級別輸出到不同文件printClassFile(level, logtxt);break;}}// 將日志輸出到指定文件void printOneFile(const std::string &logname, const std::string &logtxt) {std::string _logname = path + logname;// 以追加方式打開文件,如果不存在則創建int fd = open(_logname.c_str(), O_WRONLY | O_CREAT | O_APPEND, 0666);if (fd < 0) return;  // 打開失敗則直接返回write(fd, logtxt.c_str(), logtxt.size());  // 寫入日志內容close(fd);  // 關閉文件描述符}// 根據日志級別將日志輸出到不同文件void printClassFile(int level, const std::string &logtxt) {std::string filename = LogFile;filename += ".";filename += levelToString(level);  // 構造文件名,如"log.txt.Debug"printOneFile(filename, logtxt);}// 重載函數調用運算符,實現日志記錄的核心功能void operator()(int level, const char *format, ...) {// 1. 獲取當前時間time_t t = time(nullptr);struct tm *ctime = localtime(&t);// 2. 格式化日志頭部(時間和級別信息)char leftbuffer[SIZE];snprintf(leftbuffer, sizeof(leftbuffer), "[%s][%d-%d-%d %d:%d:%d]", levelToString(level).c_str(),ctime->tm_year + 1900, ctime->tm_mon + 1, ctime->tm_mday,ctime->tm_hour, ctime->tm_min, ctime->tm_sec);// 3. 處理可變參數,格式化日志內容va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer), format, s);va_end(s);// 4. 組合完整的日志消息char logtxt[SIZE * 2];snprintf(logtxt, sizeof(logtxt), "%s %s", leftbuffer, rightbuffer);// 5. 輸出日志printLog(level, logtxt);}
};// 創建全局日志對象,方便在程序各處使用
Log lg;/* 示例用法:
int main() {lg.Enable(Screen);  // 設置輸出到屏幕lg(Info, "Server started on port %d", 8080);lg(Error, "Failed to connect to %s", "database");return 0;
}
*/

1.3 HttpServer.hpp-網頁服務器

HttpServer.hpp

功能:

  • HTTP請求處理
  • 多線程服務
  • 靜態文件響應
  • Cookie支持
  • 錯誤頁面處理
#pragma once  // 防止頭文件重復包含// 基礎庫和系統庫引入
#include <iostream>     // 標準輸入輸出
#include <string>       // 字符串處理
#include <pthread.h>    // POSIX線程庫
#include <fstream>      // 文件流操作
#include <vector>       // 動態數組
#include <sstream>      // 字符串流
#include <sys/types.h>  // 基本系統數據類型
#include <sys/socket.h> // Socket通信
#include <unordered_map> // 哈希表// 自定義頭文件
#include "Socket.hpp"   // Socket封裝類
#include "Log.hpp"      // 日志系統// 全局常量定義
const std::string wwwroot="./wwwroot"; // web服務器根目錄
const std::string sep = "\r\n";        // HTTP消息分隔符
const std::string homepage = "index.html"; // 默認主頁static const int defaultport = 8082;    // 默認端口號class HttpServer;  // 前向聲明// 線程數據結構:存儲每個線程處理的連接信息
class ThreadData
{
public:ThreadData(int fd, HttpServer *s) : sockfd(fd), svr(s) {}public:int sockfd;         // 客戶端連接的socket描述符HttpServer *svr;    // HTTP服務器對象指針
};// HTTP請求解析類
class HttpRequest
{
public:// 反序列化HTTP請求void Deserialize(std::string req) {while(true){std::size_t pos = req.find(sep);if(pos == std::string::npos) break;std::string temp = req.substr(0, pos);if(temp.empty()) break;req_header.push_back(temp);    // 保存請求頭req.erase(0, pos+sep.size());  // 移除已處理部分}text = req;  // 保存請求體}// 解析HTTP請求,處理URL和文件路徑void Parse(){// 解析請求行(方法、URL、HTTP版本)std::stringstream ss(req_header[0]);ss >> method >> url >> http_version;// 構建文件路徑file_path = wwwroot;  if(url == "/" || url == "/index.html") {file_path += "/";file_path += homepage;  // 處理默認主頁}else file_path += url;     // 其他頁面// 獲取文件后綴auto pos = file_path.rfind(".");if(pos == std::string::npos) suffix = ".html";else suffix = file_path.substr(pos);}// 調試打印函數void DebugPrint(){// 輸出請求信息用于調試for(auto &line : req_header){std::cout << "--------------------------------" << std::endl;std::cout << line << "\n\n";}std::cout << "method: " << method << std::endl;std::cout << "url: " << url << std::endl;std::cout << "http_version: " << http_version << std::endl;std::cout << "file_path: " << file_path << std::endl;std::cout << text << std::endl;}public:std::vector<std::string> req_header;  // 請求頭部std::string text;                     // 請求正文// 解析后的請求信息std::string method;       // 請求方法(GET、POST等)std::string url;         // 請求URLstd::string http_version; // HTTP協議版本std::string file_path;   // 請求文件路徑std::string suffix;      // 文件后綴
};// HTTP服務器類
class HttpServer
{
public:// 構造函數:初始化端口和支持的內容類型HttpServer(uint16_t port = defaultport) : port_(port){content_type.insert({".html", "text/html"});content_type.insert({".png", "image/png"});}// 啟動服務器bool Start(){// 初始化Socket// 1. 創建Socketlistensock_.Socket();/* 這一步完成以下操作:a) 調用系統函數 socket(AF_INET, SOCK_STREAM, 0) 創建TCP Socket- AF_INET: 使用IPv4協議族- SOCK_STREAM: 使用TCP協議- 0: 使用默認協議b) 設置Socket選項- SO_REUSEADDR: 允許地址重用,避免服務器重啟時的"地址已被使用"錯誤*/// 2. 綁定端口listensock_.Bind(port_);/* 這一步完成以下操作:a) 創建sockaddr_in結構體,設置:- sin_family = AF_INET (IPv4)- sin_port = htons(port_) (設置端口號,轉換為網絡字節序)- sin_addr.s_addr = INADDR_ANY (監聽所有網卡接口)b) 調用bind()函數將Socket與地址綁定- 如果端口已被占用或權限不足,會失敗*/// 3. 開始監聽listensock_.Listen();/* 這一步完成以下操作:a) 調用listen()函數,將Socket轉換為監聽狀態- backlog參數設置為10,表示等待連接隊列的最大長度- 超過此長度的新連接請求會被拒絕b) 此后Socket就能接受客戶端連接請求- 服務器調用Accept()接受新的連接*/// 主循環:接受并處理連接for (;;){// 準備變量存儲客戶端信息std::string clientip;    // 將存儲客戶端的IP地址uint16_t clientport;     // 將存儲客戶端的端口號// 接受新的客戶端連接int sockfd = listensock_.Accept(&clientip, &clientport);/* Accept函數做了這些事:1. 等待客戶端連接2. 獲取客戶端的IP和端口3. 返回新的socket描述符用于與該客戶端通信*/// 連接失敗則繼續等待下一個連接if (sockfd < 0) continue;// 記錄新連接日志lg(Info, "get a new connect, sockfd: %d", sockfd);// 創建新線程處理請求// 1. 聲明線程ID變量pthread_t tid;    // 用于存儲新創建線程的ID// 2. 創建線程數據結構,傳入連接描述符和當前服務器對象ThreadData *td = new ThreadData(sockfd, this);/* ThreadData包含:- sockfd:與客戶端通信的socket描述符- this:當前服務器對象的指針,用于訪問服務器的方法*/// 3. 創建新線程處理請求pthread_create(&tid, nullptr, ThreadRun, td);/* 參數含義:- &tid:存儲新線程ID- nullptr:使用默認線程屬性- ThreadRun:線程將執行的函數- td:傳遞給線程函數的參數*/// 新線程會執行ThreadRun函數處理客戶端請求// 主線程繼續循環等待新的連接}}// 讀取HTML文件內容static std::string ReadHtmlContent(const std::string &htmlpath){// 1. 打開文件std::ifstream in(htmlpath, std::ios::binary);/* 說明:- binary模式打開確保文件按原樣讀取- 不會對換行符進行轉換*/// 文件打開失敗則返回空字符串if(!in.is_open()) return "";// 2. 獲取文件大小in.seekg(0, std::ios_base::end);   // 將讀指針移到文件末尾auto len = in.tellg();             // 獲取當前位置(即文件大小)in.seekg(0, std::ios_base::beg);   // 將讀指針移回文件開頭// 3. 讀取文件內容std::string content;           // 用于存儲文件內容content.resize(len);           // 預分配空間// 一次性讀取整個文件內容到字符串中in.read((char*)content.c_str(), content.size());// 4. 關閉文件in.close();return content;  // 返回文件內容}// 根據文件后綴獲取Content-Typestd::string SuffixToDesc(const std::string &suffix){// 在content_type映射表中查找文件后綴對應的MIME類型auto iter = content_type.find(suffix);// 如果找不到對應的MIME類型if(iter == content_type.end()) return content_type[".html"];  // 默認返回html的MIME類型:"text/html"else return content_type[suffix];   // 返回找到的MIME類型/* 例如:- .html -> "text/html"- .png  -> "image/png"這個MIME類型會被用在HTTP響應頭的Content-Type字段中*/}// 處理HTTP請求void HandlerHttp(int sockfd){// 1. 接收HTTP請求char buffer[10240];ssize_t n = recv(sockfd, buffer, sizeof(buffer) - 1, 0);/* 參數解釋:1. sockfd: 套接字描述符,用于標識與客戶端的連接2. buffer: 接收數據的緩沖區3. sizeof(buffer) - 1: 最大接收長度,預留1個字節給'\0'4. 0: 標志位,使用默認行為返回值n:- 大于0:實際接收的字節數- 等于0:連接已關閉- 小于0:接收錯誤*/if (n > 0){buffer[n] = 0;  // 字符串結束符// 2. 解析HTTP請求HttpRequest req;req.Deserialize(buffer);   // 反序列化請求內容req.Parse();               // 解析請求(獲取方法、URL、版本等)// 3. 讀取請求的文件內容std::string text;bool ok = true;text = ReadHtmlContent(req.file_path);  // 讀取請求的文件if(text.empty())  // 文件不存在或讀取失敗{ok = false;// 返回錯誤頁面std::string err_html = wwwroot + "/err.html";text = ReadHtmlContent(err_html);}// 4. 構建HTTP響應// 4.1 響應行std::string response_line;if(ok)response_line = "HTTP/1.0 200 OK\r\n";elseresponse_line = "HTTP/1.0 404 Not Found\r\n";// 4.2 響應頭std::string response_header = "Content-Length: ";response_header += std::to_string(text.size());response_header += "\r\n";response_header += "Content-Type: ";response_header += SuffixToDesc(req.suffix);  // 設置正確的MIME類型response_header += "\r\n";response_header += "Set-Cookie: name=haha&&passwd=12345";  // 設置Cookieresponse_header += "\r\n";// 4.3 空行std::string blank_line = "\r\n";// 4.4 組裝完整響應(響應行+響應頭+空行+響應體)std::string response = response_line + response_header + blank_line + text;// 5. 發送響應給客戶端send(sockfd, response.c_str(), response.size(), 0);}// 6. 關閉連接close(sockfd);}// 線程運行函數static void *ThreadRun(void *args){pthread_detach(pthread_self());  // 設置線程分離ThreadData *td = static_cast<ThreadData *>(args);td->svr->HandlerHttp(td->sockfd);delete td;return nullptr;}~HttpServer() {}private:Sock listensock_;    // 監聽socketuint16_t port_;      // 服務器端口std::unordered_map<std::string, std::string> content_type;  // 支持的內容類型映射
};

1.4 Socket.hpp-網絡通信器

Socket.hpp

功能:

  • TCP連接封裝
  • 地址綁定
  • 端口監聽
  • 客戶端連接處理
  • 錯誤處理
#pragma once  // 防止頭文件重復包含// 系統相關頭文件
#include <iostream>     // 標準輸入輸出
#include <string>       // 字符串處理
#include <unistd.h>    // UNIX標準函數定義
#include <cstring>     // C字符串處理
#include <sys/types.h> // 基本系統數據類型
#include <sys/stat.h>  // 文件狀態
#include <sys/socket.h> // Socket接口
#include <arpa/inet.h> // IP地址轉換函數
#include <netinet/in.h> // IP協議家族
#include "Log.hpp"     // 日志系統// 錯誤枚舉定義
enum
{SocketErr = 2,  // Socket創建錯誤BindErr,        // 綁定錯誤ListenErr,      // 監聽錯誤
};// 監聽隊列長度
const int backlog = 10;// Socket封裝類
class Sock
{
public:Sock() {}~Sock() {}public:// 創建Socketvoid Socket(){// 創建TCP Socketsockfd_ = socket(AF_INET, SOCK_STREAM, 0);if (sockfd_ < 0){// 創建失敗,記錄錯誤日志并退出lg(Fatal, "socker error, %s: %d", strerror(errno), errno);exit(SocketErr);}// 設置Socket選項:地址重用int opt = 1;setsockopt(sockfd_, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt));}// 綁定端口void Bind(uint16_t port){// 創建并初始化地址結構struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;           // IPv4local.sin_port = htons(port);         // 端口號local.sin_addr.s_addr = INADDR_ANY;   // 監聽所有網卡// 綁定地址和端口if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){// 綁定失敗,記錄錯誤日志并退出lg(Fatal, "bind error, %s: %d", strerror(errno), errno);exit(BindErr);}}// 開始監聽void Listen(){// 啟動監聽if (listen(sockfd_, backlog) < 0){// 監聽失敗,記錄錯誤日志并退出lg(Fatal, "listen error, %s: %d", strerror(errno), errno);exit(ListenErr);}}// 接受連接int Accept(std::string *clientip, uint16_t *clientport){// 準備接收客戶端地址信息struct sockaddr_in peer;socklen_t len = sizeof(peer);// 接受新連接int newfd = accept(sockfd_, (struct sockaddr*)&peer, &len);if(newfd < 0){// 接受連接失敗,記錄警告日志lg(Warning, "accept error, %s: %d", strerror(errno), errno);return -1;}// 獲取客戶端IP地址char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;// 獲取客戶端端口號*clientport = ntohs(peer.sin_port);return newfd;  // 返回新連接的文件描述符}// 連接服務器(客戶端使用)bool Connect(const std::string &ip, const uint16_t &port){// 準備服務器地址信息struct sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = htons(port);inet_pton(AF_INET, ip.c_str(), &(peer.sin_addr));// 建立連接int n = connect(sockfd_, (struct sockaddr*)&peer, sizeof(peer));if(n == -1) {// 連接失敗std::cerr << "connect to " << ip << ":" << port << " error" << std::endl;return false;}return true;  // 連接成功}// 關閉Socketvoid Close(){close(sockfd_);}// 獲取Socket文件描述符int Fd(){return sockfd_;}private:int sockfd_;  // Socket文件描述符
};

1.5 HttpServer.cc-服務器啟動器

HttpServer.cc

功能:

  • 程序入口
  • 參數解析
  • 服務器初始化
  • 智能指針管理
// 包含必要的頭文件
#include "HttpServer.hpp"  // HTTP服務器類定義
#include <iostream>        // 標準輸入輸出
#include <memory>         // 智能指針
#include <pthread.h>      // POSIX線程庫
#include "Log.hpp"        // 日志系統using namespace std;int main(int argc, char *argv[])
{// 檢查命令行參數if(argc != 2)  // 要求必須提供端口號參數{exit(1);   // 參數錯誤,退出程序}// 將命令行參數轉換為端口號uint16_t port = std::stoi(argv[1]);  // 字符串轉換為整數// 創建HTTP服務器實例// 以下是三種方式,注釋掉的是不推薦的方式// 方式1(不推薦):普通指針,需要手動管理內存// HttpServer *svr = new HttpServer();// 方式2(語法錯誤):unique_ptr的錯誤聲明方式// std::unique<HttpServer> svr(new HttpServer());// 方式3(推薦):使用智能指針unique_ptr,自動管理內存std::unique_ptr<HttpServer> svr(new HttpServer(port));// 啟動服務器svr->Start();  // 開始監聽和處理請求// 程序正常退出return 0;
}

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

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

相關文章

沐渥科技詳解氮氣柜操作指南

氮氣柜是一種通過持續注入高純度氮氣&#xff0c;維持柜內惰性氣體環境的設備&#xff0c;用于存儲半導體晶圓或其他敏感元件&#xff0c;防止氧化、吸濕和污染。氮氣柜操作指南是怎樣的&#xff1f;下面沐渥小編給大家介紹一下。 一、操作前準備 &#xff08;1&#xff09;安全…

從零實現Agent智能體配置使用(Ragflow)

從零實現Agent智能體配置使用&#xff08;Ragflow&#xff09; 1. 創建智能體2. 配置智能體2.1 配置問題識別2.2 配置問題分類2.3 不同問題進行單獨配置2.4 保存Agent 3. 體驗效果 1. 創建智能體 2. 配置智能體 2.1 配置問題識別 2.2 配置問題分類 2.3 不同問題進行單獨配置 當…

顯示器各類異常處理方法

顯示器各類異常處理方法 導航 文章目錄 顯示器各類異常處理方法導航畫面無顯示/黑屏/無HDMI信號輸入顯示器閃爍顯示器花屏顯示畫面模糊或扭曲顯示器顏色異常顯示器出現死點或亮點 畫面無顯示/黑屏/無HDMI信號輸入 ? 首先應該檢查的是顯示器電源&#xff08;真的有人弄掉電源…

原理剖析 + 實戰教程 + 資源優化總結大模型微調實戰:LoRA / QLoRA / PEFT 全解析,教你低成本玩轉大模型微調

隨著大語言模型&#xff08;LLM&#xff09;在自然語言處理各領域取得突破性進展&#xff0c;越來越多開發者和企業開始關注模型的微調方式。然而&#xff0c;全參數微調不僅成本高昂、資源要求極高&#xff0c;還容易引發過擬合與知識遺忘等問題。為此&#xff0c;LoRA、QLoRA…

Higress: 阿里巴巴高性能云原生API網關詳解

一、Higress概述 Higress是阿里巴巴開源的一款基于云原生技術構建的高性能API網關&#xff0c;專為Kubernetes和微服務架構設計。它集成了Ingress控制器、微服務網關和API網關功能于一體&#xff0c;支持多種協議和豐富的流量管理能力。 發展歷程 Higress 從最初社區的 Isti…

解決 IntelliJ IDEA 中 Maven 項目左側項目視圖未顯示頂層目錄問題的詳細步驟說明

以下是解決 IntelliJ IDEA 中 Maven 項目左側項目視圖未顯示頂層目錄問題的詳細步驟說明&#xff1a; 1. 切換項目視圖模式 默認情況下&#xff0c;IDEA 的項目視圖可能處于 Packages 模式&#xff0c;僅顯示代碼包結構&#xff0c;而非物理目錄。 操作步驟&#xff1a; 點擊…

【Vue-vue基礎知識】學習筆記

目錄 <<回到導覽vue基礎知識1.1.創建一個vue實例1.2.vue基礎指令1.2.1.v-bind1.2.2.v-model1.2.3.常用事件1.2.4.指令修飾符 1.3.計算屬性1.3.1.計算屬性的完整寫法1.3.2.【案例】成績 1.4.watch1.4.1.watch屬性1.4.2.翻譯業務實現1.4.3.watch屬性的完整寫法1.4.4.【案例…

Element Plus 圖標使用方式整理

Element Plus 圖標使用方式整理 以下是 Element Plus 圖標的所有使用方式&#xff0c;包含完整代碼示例和總結表格&#xff1a; 1. 按需引入圖標組件 適用場景&#xff1a;僅需少量圖標時&#xff0c;按需導入減少打包體積 示例代碼&#xff1a; <template><div>…

使用Scrapy官方開發的爬蟲部署、運行、管理工具:Scrapyd

一般情況下&#xff0c;爬蟲會使用云服務器來運行&#xff0c;這樣可以保證爬蟲24h不間斷運行。但是如何把爬蟲放到云服務器上面去呢&#xff1f;有人說用FTP&#xff0c;有人說用Git&#xff0c;有人說用Docker。但是它們都有很多問題。 FTP&#xff1a;使用FTP來上傳…

41、web前端開發之Vue3保姆教程(五 實戰案例)

一、項目簡介和需求概述 1、項目目標 1.能夠基于Vue3創建項目 2.能夠基本Vue3相關的技術棧進行項目開發 3.能夠使用Vue的第三方組件進行項目開發 4.能夠理解前后端分離的開發模式 2、項目概述 使用Vue3結合ElementPlus,ECharts工具實現后臺管理系統頁面,包含登錄功能,…

OpenCV--圖像平滑處理

在數字圖像處理領域&#xff0c;圖像平滑處理是一項極為重要的技術&#xff0c;廣泛應用于計算機視覺、醫學影像分析、安防監控等多個領域。在 OpenCV 這一強大的計算機視覺庫的助力下&#xff0c;我們能便捷地實現多種圖像平滑算法。本文將深入探討圖像平滑的原理&#xff0c;…

性能優化利器:前后端防抖方案解析

精心整理了最新的面試資料和簡歷模板&#xff0c;有需要的可以自行獲取 點擊前往百度網盤獲取 點擊前往夸克網盤獲取 在Web開發中&#xff0c;高頻觸發的事件&#xff08;如用戶輸入、按鈕點擊、滾動監聽等&#xff09;可能導致性能問題或資源浪費。防抖&#xff08;Debounce&…

【ES系列】Elasticsearch簡介:為什么需要它?(基礎篇)

?? 本文將詳細介紹Elasticsearch的前世今生,以及為什么它在當今的技術棧中如此重要。本文是ES起飛之路系列的基礎篇第一章,適合想要了解ES的讀者。 文章目錄 一、什么是Elasticsearch?1. ES的定義2. ES的核心特性2.1 分布式存儲2.2 實時搜索2.3 高可用性2.4 RESTful API3.…

用 HTML 網頁來管理 Markdown 標題序號

文章目錄 工具介紹核心優勢使用指南基本使用方法注意事項 部分截圖完整代碼 工具介紹 在日常的文檔編寫和博客創作中&#xff0c;Markdown因其簡潔的語法和良好的可讀性而廣受歡迎。然而&#xff0c;當文檔結構復雜、標題層級較多時&#xff0c;手動維護標題序號不僅耗時耗力&…

批量將 Markdown 轉換為 Word/PDF 等其它格式

在工作當中&#xff0c;我們經常會接觸到 Markdown 格式的文檔。這是一種非常方便我們做記錄&#xff0c;做筆記的一種格式文檔。現在很多互聯網編輯器都是支持 Markdown 格式的&#xff0c;編輯起文章來更加的方便簡介。有時候&#xff0c;我們會碰到需要將 Markdown 格式的文…

劍指Offer(數據結構與算法面試題精講)C++版——day8

劍指Offer&#xff08;數據結構與算法面試題精講&#xff09;C版——day8 題目一&#xff1a;鏈表中環的入口節點題目二&#xff1a;兩個鏈表的第1個重合節點題目三&#xff1a;反轉鏈表附錄&#xff1a;源碼gitee倉庫 題目一&#xff1a;鏈表中環的入口節點 這道題的有如下三個…

【BFT帝國】20250409更新PBFT總結

2411 2411 2411 Zhang G R, Pan F, Mao Y H, et al. Reaching Consensus in the Byzantine Empire: A Comprehensive Review of BFT Consensus Algorithms[J]. ACM COMPUTING SURVEYS, 2024,56(5).出版時間: MAY 2024 索引時間&#xff08;可被引用&#xff09;: 240412 被引:…

前端用用jsonp的方式解決跨域問題

前端用用jsonp的方式解決跨域問題 前端用用jsonp的方式解決跨域問題 前端用用jsonp的方式解決跨域問題限制與缺點&#xff1a;前端后端測試使用示例 限制與缺點&#xff1a; 不安全、只能使用get方式、后臺需要相應jsonp方式的傳參 前端 function jsonp(obj) {// 動態生成唯…

MySQL詳解最新的官方備份方式Clone Plugin

一、Clone Plugin的動態安裝 install plugin clone soname mysql_clone.so;select plugin_name,plugin_status from information_schema.plugins where plugin_name clone; 二、Clone Plugin配置持久化 在 MySQL 配置文件my.cnf中添加以下內容&#xff0c;確保插件在 MySQL …

解決python manage.py shell ModuleNotFoundError: No module named xxx

報錯如下&#xff1a; python manage.py shellTraceback (most recent call last):File "/Users/z/Documents/project/c/manage.py", line 10, in <module>execute_from_command_line(sys.argv)File "/Users/z/.virtualenvs/c/lib/python3.12/site-packa…