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

从零开始实现 C++ TinyWebServer 处理请求 HttpRequest类详解

文章目录

  • HTTP 请求报文
  • HttpRequest 类
  • 实现 Init() 函数
  • 实现 ParseRequestLine() 函数
  • 实现 ParseHeader() 函数
  • 实现 ParsePath() 函数
  • 实现 ParseBody() 函数
  • 实现 ParsePost() 函数
  • 实现 ParseFromUrlEncoded() 函数
  • 实现 UserVerify() 函数
  • 实现 Parse() 函数
  • HttpRequest 代码
  • HttpRequest 测试

从零开始实现 C++ TinyWebServer 项目总览
项目源码

HTTP 请求报文

 2025-02-27 200519.png

 2025-02-27 200654.png

一个典型的 HTTP 请求包含请求行、请求头和请求体(可选)。

  • 请求行:包含请求方法(如 GETPOST 等)、请求路径(如 /index.html)和 HTTP 版本(如 HTTP/1.1)。
  • 请求头:包含多个键值对,用于传递额外的信息,如 HostUser-Agent 等。
  • 请求体:通常在 POST 请求中使用,用于传递数据。

HttpRequest 类

在 Web 服务器中,HttpRequest类起着至关重要的作用,主要负责处理客户端发送的 HTTP 请求。具体来说,它承担了以下几个关键任务:

  1. 请求解析:接收客户端发送的 HTTP 请求报文,并对其进行解析,将请求报文中的各个部分(如请求行、请求头、请求体等)提取出来,以便后续处理。
  2. 状态管理:在解析过程中,通过状态机来管理解析的状态,确保解析过程的正确性和顺序性。
  3. 请求处理:根据解析结果,进行相应的处理,例如处理 GET 请求、POST 请求等,并根据请求的内容和服务器的配置,决定如何响应客户端。
class HttpRequest {
public:enum PARSE_STATE {REQUEST_LINE,HEADERS,BODY,FINISH};HttpRequest() { Init(); }~HttpRequest() = default;void Init();bool Parse(Buffer& buff);std::string Method() const;std::string Path() const;std::string& Path();std::string Version() const;std::string GetPost(const std::string& key) const;std::string GetPost(const char* key) const;bool IsKeepAlive() const;private:static int ConverHex(char ch);	// 16进制转10进制static bool UserVerify(const std::string& name, const std::string& pwd, bool is_login);	// 验证用户bool ParseRequestLine(const std::string& line);	// 解析请求行void ParseHeader(const std::string& line);		// 解析请求头void ParseBody(const std::string& line);		// 解析请求体void ParsePath();	// 解析请求路径void ParsePost();	// 解析Post事件void ParseFromUrlEncoded();	// 解析urlstatic const std::unordered_set<std::string> DEFAULT_HTML;static const std::unordered_map<std::string, int> DEFAULT_HTML_TAG;PARSE_STATE state_;std::string method_;std::string path_;std::string version_;std::string body_;std::unordered_map<std::string, std::string> header_; // HTTP 请求的头部信息std::unordered_map<std::string, std::string> post_;   // POST 请求的数据
};

实现 Init() 函数

void HttpRequest::Init() {state_ = REQUEST_LINE;method_ = path_ = version_ = body_ = "";header_.clear();post_.clear();
}

实现 ParseRequestLine() 函数

  • 使用正则表达式^([^ ]*) ([^ ]*) HTTP/([^ ]*)$以及std::regex_match来匹配请求行。
  • 若匹配成功,将匹配结果分别存储到method_path_version_中,并将解析状态设置为HEADERS,表示接下来要解析请求头,返回true
bool HttpRequest::ParseRequestLine(const std::string& line) {// GET /index.html HTTP/1.1std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");std::smatch match;// 匹配指定字符串整体是否符合if (std::regex_match(line, match, patten)) { method_ = match[1];path_ = match[2];version_ = match[3];state_ = HEADERS;return true;} LOG_ERROR("RequestLine Error"); return false;
}

实现 ParseHeader() 函数

  • 如果路径为/,则将其修改为/index.html
  • 否则,检查路径是否在DEFAULT_HTML中,如果存在则在路径后面添加.html后缀。
void HttpRequest::ParseHeader(const std::string& line) {std::regex patten("^([^:]*): ?(.*)$");std::smatch match;if (std::regex_match(line, match, patten)) {header_[match[1]] = match[2];} else {state_ = BODY;}
}

实现 ParsePath() 函数

  • 如果路径为/,则将其修改为/index.html
  • 否则,检查路径是否在DEFAULT_HTML中,如果存在则在路径后面添加.html后缀
void HttpRequest::ParsePath() {if (path_ == "/") {path_ = "/index.html";} else {if (DEFAULT_HTML.find(path_) != DEFAULT_HTML.end())path_ += ".html";}
}

实现 ParseBody() 函数

  • 将请求体内容存储到body_中。
  • 调用ParsePost函数处理 POST 请求数据。
  • 将解析状态设置为FINISH,表示解析完成。
void HttpRequest::ParseBody(const std::string& line) {body_ = line;ParsePost();    state_ = FINISH;LOG_DEBUG("Body: %s, len: %d", line.c_str(), line.size());
}

实现 ParsePost() 函数

  • 检查请求方法是否为POST,并且请求头中的Content-Type是否为application/x-www-form-urlencoded
  • 如果满足条件,调用ParseFromUrlencoded函数解析 URL 编码的请求体。
  • 检查路径是否为登录或注册相关路径,如果是则调用UserVerify函数,进行用户验证,根据验证结果设置相应的跳转路径。
void HttpRequest::ParsePost() {if (method_ == "POST" && header_["Content-Type"] == "application/x-www-form-urlencoded") {ParseFromUrlEncoded();if (DEFAULT_HTML_TAG.count(path_)) { // 登录/注册int tag = DEFAULT_HTML_TAG.find(path_)->second;LOG_DEBUG("Tag: %d", tag);if (tag == 0 || tag == 1) {bool is_login = (tag == 1);if (UserVerify(post_["username"], post_["password"], is_login))path_ = "/welcome.html";elsepath_ = "/error.html";}}}
}

实现 ParseFromUrlEncoded() 函数

  • 首先检查请求体是否为空,如果为空则直接返回。
  • 遍历请求体字符串,根据不同的字符进行不同的处理:
    • 遇到=,将之前的字符作为键存储到key中,并更新起始位置j
    • 遇到&,将之前的字符作为值存储到value中,并将键值对存储到post_容器中,同时更新起始位置j
    • 遇到+,将其替换为空格。
    • 遇到%,将其后的两个十六进制字符转换为十进制整数,并更新请求体内容。
  • 最后处理剩余的键值对。
void HttpRequest::ParseFromUrlEncoded() {if (body_.size() == 0)return;std::string key, value;int num = 0;int n = body_.size();int i = 0, j = 0;while (i < n) {char ch = body_[i];// username=john%20doe&password=123456switch (ch) {case '=': // 获取键值对key = body_.substr(j, i - j); j = i + 1;break;case '&': // 获取键值对value = body_.substr(j, i - j);j = i + 1;post_[key] = value;LOG_DEBUG("%s = %s", key.c_str(), value.c_str());break;case '+': // 替换为空格body_[i] = ' ';break;case '%':num = ConverHex(body_[i + 1]) * 16 + ConverHex(body_[i + 2]);body_[i + 2] = num % 10 + '0';body_[i + 1] = num / 10 + '0';i += 2;break;default:break;}i++;}assert(j <= i);if (post_.count(key) == 0 && j < i) {value = body_.substr(j, i - j);post_[key] = value;}
}

实现 UserVerify() 函数

  • 首先检查用户名和密码是否为空,如果为空则返回false
  • 使用SqlConnRAII管理数据库连接,构造查询语句,查询数据库中是否存在该用户名。
  • 如果是登录行为,比较输入的密码和数据库中的密码是否一致,一致则验证成功。
  • 如果是注册行为,检查用户名是否已被使用,若未被使用则将用户信息插入数据库。
// 验证用户的用户名和密码
bool HttpRequest::UserVerify(const std::string& name, const std::string& pwd, bool is_login) {if (name == "" || pwd == "")return false;LOG_INFO("Verify name: %s pwd: %s", name.c_str(), pwd.c_str());MYSQL* sql;SqlConnectRAII(&sql, SqlConnectPool::GetInstance());assert(sql);bool flag = false;char order[256] = {0};MYSQL_RES* res = nullptr;      // 查询结果集if (!is_login) // 注册flag = true;snprintf(order, 256, "SELECT username, password FROM User WHERE username='%s' LIMIT 1", name.c_str());LOG_DEBUG("%s", order);// 查询用户信息if (mysql_query(sql, order)) {if (res)mysql_free_result(res); // 查询失败,释放结果集return false;}res = mysql_store_result(sql);   // 存储查询结果到res中// 处理查询结果while (MYSQL_ROW row = mysql_fetch_row(res)) {LOG_DEBUG("MYSQL ROW: %s %s", row[0], row[1]);std::string password = row[1];if (is_login) {if (pwd == password) {flag = true;} else {flag = false;LOG_INFO("pwd error!");}} else {flag = false;LOG_INFO("user used!");}}mysql_free_result(res);// 注册(用户名未被使用)if (!is_login && flag) {LOG_DEBUG("register");bzero(order, 256);snprintf(order, 256, "INSERT INTO User(username, password) VALUES('%s', '%s')", name.c_str(), pwd.c_str());LOG_DEBUG("%s", order);if (mysql_query(sql, order)) {LOG_ERROR("MySQL insert fail: Error: %s", mysql_error(sql));flag = false;} else {LOG_DEBUG("UserVerify success!");flag = true;}}return flag;
}

实现 Parse() 函数

  • 首先检查Buffer中是否有可读数据,如果没有则返回false

  • 循环读取Buffer中的数据,直到没有可读数据或者解析状态为FINISH

  • 利用search函数查找\r\n来确定每行数据的结束位置,将每行数据提取出来存为line

  • 根据当前解析状态state_的不同,调用不同的解析函数进行处理:

    • 若为REQUEST_LINE状态,调用ParseRequestLine_解析请求行,若解析失败则返回false,成功则继续解析路径。
    • 若为HEADERS状态,调用ParseHeader_解析请求头,若Buffer中剩余可读字节小于等于 2,则认为是 GET 请求,将状态设置为FINISH
    • 若为BODY状态,调用ParseBody_解析请求体。
  • 如果lineend到达Buffer的写指针位置,说明数据读完,调用buff.RetrieveAll()清空Buffer,并跳出循环;否则跳过\r\n继续解析。

  • 最后记录解析出的请求方法、路径和版本信息,并返回true表示解析成功。

bool HttpRequest::Parse(Buffer& buff) {const char* END = "\r\n";if (buff.ReadableBytes() == 0)return false;while (buff.ReadableBytes() && state_ != FINISH) {// 找到buff中,首次出现"\r\n"的位置const char* line_end = std::search(buff.ReadBegin(), buff.WriteBeginConst(), END, END + 2);string line(buff.ReadBegin(), line_end);switch (state_) {case REQUEST_LINE:if (ParseRequestLine(line) == false)return false;ParsePath();break;case HEADERS:ParseHeader(line);if (buff.ReadableBytes() <= 2) // get请求,提前结束state_ = FINISH;break;case BODY:ParseBody(line);break;default:break;}if (line_end == buff.WriteBegin()) { // 读完了buff.RetrieveAll();break;}buff.RetrieveUntil(line_end + 2); // 跳过回车换行}LOG_DEBUG("[%s] [%s] [%s]", method_ .c_str(), path_.c_str(), version_.c_str());return true;
}

HttpRequest 代码

http_request.h

#ifndef HTTP_REQUEST_H
#define HTTP_REQUEST_H#include <cstring>
#include <string>
#include <unordered_set>
#include <unordered_map>
#include <algorithm>
#include <regex> // 正则表达式
#include <mysql/mysql.h>#include "../buffer/buffer.h"
#include "../log/log.h"
#include "../pool/sql_connect_pool.h"class HttpRequest {
public:enum PARSE_STATE {REQUEST_LINE,HEADERS,BODY,FINISH};HttpRequest() { Init(); }~HttpRequest() = default;void Init();bool Parse(Buffer& buff);std::string Method() const;std::string Path() const;std::string& Path();std::string Version() const;std::string GetPost(const std::string& key) const;std::string GetPost(const char* key) const;bool IsKeepAlive() const;private:static int ConverHex(char ch);static bool UserVerify(const std::string& name, const std::string& pwd, bool is_login);bool ParseRequestLine(const std::string& line);void ParseHeader(const std::string& line);void ParseBody(const std::string& line);void ParsePath();void ParsePost();void ParseFromUrlEncoded();static const std::unordered_set<std::string> DEFAULT_HTML;static const std::unordered_map<std::string, int> DEFAULT_HTML_TAG;PARSE_STATE state_;std::string method_;std::string path_;std::string version_;std::string body_;std::unordered_map<std::string, std::string> header_; // HTTP 请求的头部信息std::unordered_map<std::string, std::string> post_;   // POST 请求的数据
};#endif // HTTP_REQUEST_H

http_request.cc

#include "http_request.h"const std::unordered_set<std::string> HttpRequest::DEFAULT_HTML {"/index", "/register", "/login", "/welcome", "/video", "/picture"
};// 登录/注册
const std::unordered_map<std::string, int> HttpRequest::DEFAULT_HTML_TAG {{"/login.html", 1}, {"/register.html", 0}
};void HttpRequest::Init() {state_ = REQUEST_LINE;method_ = path_ = version_ = body_ = "";header_.clear();post_.clear();
}bool HttpRequest::ParseRequestLine(const std::string& line) {// GET /index.html HTTP/1.1std::regex patten("^([^ ]*) ([^ ]*) HTTP/([^ ]*)$");std::smatch match;if (std::regex_match(line, match, patten)) {method_ = match[1];path_ = match[2];version_ = match[3];state_ = HEADERS;return true;} LOG_ERROR("RequestLine Error"); return false;
}void HttpRequest::ParseHeader(const std::string& line) {std::regex patten("^([^:]*): ?(.*)$");std::smatch match;if (std::regex_match(line, match, patten)) {header_[match[1]] = match[2];} else {state_ = BODY;}
}void HttpRequest::ParseBody(const std::string& line) {body_ = line;ParsePost();    state_ = FINISH;LOG_DEBUG("Body: %s, len: %d", line.c_str(), line.size());
}void HttpRequest::ParsePost() {if (method_ == "POST" && header_["Content-Type"] == "application/x-www-form-urlencoded") {ParseFromUrlEncoded();if (DEFAULT_HTML_TAG.count(path_)) { // 登录/注册int tag = DEFAULT_HTML_TAG.find(path_)->second;LOG_DEBUG("Tag: %d", tag);if (tag == 0 || tag == 1) {bool is_login = (tag == 1);if (UserVerify(post_["username"], post_["password"], is_login))path_ = "/welcome.html";elsepath_ = "/error.html";}}}
}// 16进制转10进制
int HttpRequest::ConverHex(char ch) {if (ch >= 'A' && ch <= 'F')return ch - 'A' + 10;if (ch >= 'a' && ch <= 'f')return ch - 'a' + 10;return ch;
}void HttpRequest::ParseFromUrlEncoded() {if (body_.size() == 0)return;std::string key, value;int num = 0;int n = body_.size();int i = 0, j = 0;while (i < n) {char ch = body_[i];// username=john%20doe&password=123456switch (ch) {case '=': // 获取键值对key = body_.substr(j, i - j); j = i + 1;break;case '&': // 获取键值对value = body_.substr(j, i - j);j = i + 1;post_[key] = value;LOG_DEBUG("%s = %s", key.c_str(), value.c_str());break;case '+': // 替换为空格body_[i] = ' ';break;case '%':num = ConverHex(body_[i + 1]) * 16 + ConverHex(body_[i + 2]);body_[i + 2] = num % 10 + '0';body_[i + 1] = num / 10 + '0';i += 2;break;default:break;}i++;}assert(j <= i);if (post_.count(key) == 0 && j < i) {value = body_.substr(j, i - j);post_[key] = value;}
}void HttpRequest::ParsePath() {if (path_ == "/") {path_ = "/index.html";} else {if (DEFAULT_HTML.find(path_) != DEFAULT_HTML.end())path_ += ".html";}
}// 验证用户的用户名和密码
bool HttpRequest::UserVerify(const std::string& name, const std::string& pwd, bool is_login) {if (name == "" || pwd == "")return false;LOG_INFO("Verify name: %s pwd: %s", name.c_str(), pwd.c_str());MYSQL* sql;SqlConnectRAII(&sql, SqlConnectPool::GetInstance());assert(sql);bool flag = false;char order[256] = {0};MYSQL_RES* res = nullptr;      // 查询结果集if (!is_login) // 注册flag = true;snprintf(order, 256, "SELECT username, password FROM User WHERE username='%s' LIMIT 1", name.c_str());LOG_DEBUG("%s", order);// 查询用户信息if (mysql_query(sql, order)) {if (res)mysql_free_result(res); // 查询失败,释放结果集return false;}res = mysql_store_result(sql);   // 存储查询结果到res中// 处理查询结果while (MYSQL_ROW row = mysql_fetch_row(res)) {LOG_DEBUG("MYSQL ROW: %s %s", row[0], row[1]);std::string password = row[1];if (is_login) {if (pwd == password) {flag = true;} else {flag = false;LOG_INFO("pwd error!");}} else {flag = false;LOG_INFO("user used!");}}mysql_free_result(res);// 注册(用户名未被使用)if (!is_login && flag) {LOG_DEBUG("register");bzero(order, 256);snprintf(order, 256, "INSERT INTO User(username, password) VALUES('%s', '%s')", name.c_str(), pwd.c_str());LOG_DEBUG("%s", order);if (mysql_query(sql, order)) {LOG_ERROR("MySQL insert fail: Error: %s", mysql_error(sql));flag = false;} else {LOG_DEBUG("UserVerify success!");flag = true;}}return flag;
}bool HttpRequest::Parse(Buffer& buff) {const char* END = "\r\n";if (buff.ReadableBytes() == 0)return false;while (buff.ReadableBytes() && state_ != FINISH) {// 找到buff中,首次出现"\r\n"的位置const char* line_end = std::search(buff.ReadBegin(), buff.WriteBeginConst(), END, END + 2);string line(buff.ReadBegin(), line_end);switch (state_) {case REQUEST_LINE:if (ParseRequestLine(line) == false)return false;ParsePath();break;case HEADERS:ParseHeader(line);if (buff.ReadableBytes() <= 2) // get请求,提前结束state_ = FINISH;break;case BODY:ParseBody(line);break;default:break;}if (line_end == buff.WriteBegin()) { // 读完了buff.RetrieveAll();break;}buff.RetrieveUntil(line_end + 2); // 跳过回车换行}LOG_DEBUG("[%s] [%s] [%s]", method_ .c_str(), path_.c_str(), version_.c_str());return true;
}std::string HttpRequest::Method() const {return method_;
}std::string HttpRequest::Path() const {return path_;
}std::string& HttpRequest::Path() {return path_;
}std::string HttpRequest::Version() const {return version_;
}std::string HttpRequest::GetPost(const std::string& key) const {if (post_.count(key) == 1)// return post_[key]; post_有const属性,所以不能用[]return post_.find(key)->second;return "";
}std::string HttpRequest::GetPost(const char* key) const {assert(key != nullptr);if (post_.count(key) == 1)return post_.find(key)->second;return "";
}bool HttpRequest::IsKeepAlive() const {if (header_.count("Connect") == 1) return header_.find("Connect")->second == "keep-alive" && version_ == "1.1";return false;
}

HttpRequest 测试

测试HttpRequest的,解析,注册,登录功能

#include "../code/http/http_request.h"
#include <iostream>void TestHttpRequest() {// 初始化日志系统Log* logger = Log::GetInstance();logger->Init(0, "./logs/", ".log", 1024);// 初始化测试HttpRequest request;// 初始化数据库连接池SqlConnectPool* conn_pool = SqlConnectPool::GetInstance();conn_pool->Init("localhost", 3306, "Tian", "123456", "web_server", 10);// 解析请求测试std::string http_request = "GET /index.html HTTP/1.1\r\n""Host: example.com\r\n""Connection: keep-alive\r\n""\r\n";Buffer buff;buff.Append(http_request);bool parseResult = request.Parse(buff);assert(parseResult);// 访问器方法测试assert(request.Method() == "GET");assert(request.Path() == "/index.html");assert(request.Version() == "1.1");// 模拟注册 POST 请求std::string register_request = "POST /register.html HTTP/1.1\r\n""Host: example.com\r\n""Content-Type: application/x-www-form-urlencoded\r\n""Content-Length: 23\r\n""\r\n""username=test&password=123456";request.Init();buff.Append(register_request);parseResult = request.Parse(buff);assert(parseResult);assert(request.Method() == "POST");assert(request.Path() == "/welcome.html");assert(request.Version() == "1.1");assert(request.GetPost("username") == "test");assert(request.GetPost("password") == "123456");// Post 数据获取测试// 模拟登录 POST 请求std::string login_request = "POST /login HTTP/1.1\r\n""Content-Type: application/x-www-form-urlencoded\r\n""Content-Length: 19\r\n""\r\n""username=test&password=123456""\r\n";buff.Append(login_request);request.Init();parseResult = request.Parse(buff);assert(parseResult);assert(request.Method() == "POST");assert(request.Path() == "/welcome.html");assert(request.Version() == "1.1");assert(request.GetPost("username") == "test");assert(request.GetPost("password") == "123456");std::cout << "All tests passed!" << std::endl;
}int main() {TestHttpRequest();return 0;
}

CMakeLists.txt

cmake_minimum_required(VERSION 3.10)
project(tests)# 设置 C++ 标准和编译器选项
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")# 定义公共源文件和特定文件
set(COMMON_SOURCES ../code/buffer/buffer.cc ../code/log/log.cc)
set(HTTP_SOURCE ../code/http/http_request.cc)
set(POOL_SOURCE ../code/pool/sql_connect_pool.cc)# 查找 MySQL 库
find_package(PkgConfig REQUIRED)
pkg_check_modules(MYSQL REQUIRED mysqlclient)
# 包含 MySQL 头文件目录
include_directories(${MYSQL_INCLUDE_DIR})# 添加可执行文件
add_executable(http_request_test http_request_test.cc ${COMMON_SOURCES} ${HTTP_SOURCE} ${POOL_SOURCE})
# 链接库
target_link_libraries(http_request_test ${MYSQL_LIBRARIES})

return 0;
}


**CMakeLists.txt**```cmake
cmake_minimum_required(VERSION 3.10)
project(tests)# 设置 C++ 标准和编译器选项
set(CMAKE_BUILD_TYPE Debug)
set(CMAKE_CXX_STANDARD 14)
set(CMAKE_CXX_STANDARD_REQUIRED ON)
set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} -Wall -Wextra")# 定义公共源文件和特定文件
set(COMMON_SOURCES ../code/buffer/buffer.cc ../code/log/log.cc)
set(HTTP_SOURCE ../code/http/http_request.cc)
set(POOL_SOURCE ../code/pool/sql_connect_pool.cc)# 查找 MySQL 库
find_package(PkgConfig REQUIRED)
pkg_check_modules(MYSQL REQUIRED mysqlclient)
# 包含 MySQL 头文件目录
include_directories(${MYSQL_INCLUDE_DIR})# 添加可执行文件
add_executable(http_request_test http_request_test.cc ${COMMON_SOURCES} ${HTTP_SOURCE} ${POOL_SOURCE})
# 链接库
target_link_libraries(http_request_test ${MYSQL_LIBRARIES})

相关文章:

从零开始实现 C++ TinyWebServer 处理请求 HttpRequest类详解

文章目录 HTTP 请求报文HttpRequest 类实现 Init() 函数实现 ParseRequestLine() 函数实现 ParseHeader() 函数实现 ParsePath() 函数实现 ParseBody() 函数实现 ParsePost() 函数实现 ParseFromUrlEncoded() 函数实现 UserVerify() 函数实现 Parse() 函数HttpRequest 代码Http…...

Android开发layer-list

Android开发layer-list 它的用处可以在drawable上进行多图拼接&#xff0c;比如启动页&#xff0c;不想图片被拉伸就这么做。还有做某些线突出来。 示例代码&#xff1a; <?xml version"1.0" encoding"utf-8"?> <layer-list xmlns:android&q…...

如何提升库存系统的高并发和稳定性:算法与设计模式

库存系统是企业运营的核心模块&#xff0c;尤其是在电商、零售和供应链管理中&#xff0c;系统的高并发和稳定性直接影响订单处理的准确性和效率。面对海量订单、复杂的库存管理需求&#xff0c;如何在高并发环境下确保库存数据的准确性和系统的稳定性&#xff1f;本文将从架构…...

关于CNN,RNN,GAN,GNN,DQN,Transformer,LSTM,DBN你了解多少

以下是神经网络中常见的几种模型的简要介绍&#xff1a; 1. ​CNN (Convolutional Neural Network, 卷积神经网络) ​用途: 主要用于图像处理和计算机视觉任务。​特点: 通过卷积核提取局部特征&#xff0c;具有平移不变性&#xff0c;能够有效处理高维数据&#xff08;如图像…...

设计模式之装饰器模式

装饰器模式(Decorator)依然是我们设计模式中的结构型模式&#xff0c;其中的构造思想仍然是对多个类进行组合使用&#xff0c;以达成系统调用实现指定功能的设计模式。装饰器模式不论在我们日常开发过程中还是在我们提升技术阅读源码过程中都是比较常见的&#xff0c;但是整体学…...

Mistral AI发布开源多模态模型Mistral Small 3.1:240亿参数实现超越GPT-4o Mini的性能

法国人工智能初创公司Mistral AI于2025年3月正式推出新一代开源模型Mistral Small 3.1 &#xff0c;该模型凭借240亿参数的轻量级设计&#xff0c;在多项基准测试中表现优异&#xff0c;甚至超越了Google的Gemma 3和OpenAI的GPT-4o Mini等主流专有模型。 1、核心特性与优势 多…...

SpringBoot3实战(SpringBoot3+Vue3基本增删改查、前后端通信交互、配置后端跨域请求、数据批量删除(超详细))(3)

目录 一、从0快速搭建SpringBoot3工程、SpringBoot3集成MyBatis、PageHelper分页查询的详细教程。(博客链接) 二、实现前端与后端通信对接数据。(axios工具) &#xff08;1&#xff09;安装axios。(vue工程目录) &#xff08;2&#xff09;封装请求工具类。(request.js) <1&…...

LabVIEW发电平台数据采集系统

本文详细介绍了基于LabVIEW的摇臂式波浪发电平台数据采集系统的设计与实现。通过整合LabVIEW软件与多种传感器技术&#xff0c;本系统能够有效提升数据采集的准确性和效率&#xff0c;为波浪能的利用和发电设备的优化提供科学依据。 ​ 项目背景 随着全球能源需求增长和环境保…...

人工智能(AI)系统化学习路线

一、为什么需要系统化学习AI&#xff1f; 人工智能技术正在重塑各行各业&#xff0c;但许多初学者容易陷入误区&#xff1a; ❌ 盲目跟风&#xff1a;直接学习TensorFlow/PyTorch&#xff0c;忽视数学与算法基础。 ❌ 纸上谈兵&#xff1a;只看理论不写代码&#xff0c;无法解…...

oracle事务的组成

1)数据库事务由以下的部分组成: 一个或多个DML 语句 ; 一个 DDL(Data Definition Language – 数据定义语言) 语句&#xff1b; 一个 DCL(Data Control Language – 数据控制语言)语句&#xff1b; 2)事务的执行开始&#xff1a; 以第一个 DML 语句的执行作为开始 &#xff0c;…...

第二十八篇 数据获取与数据分析:数仓体系下的专业化分工与协同

声明&#xff1a;文章内容仅供参考&#xff0c;需仔细甄别。文中技术名称属相关方商标&#xff0c;仅作技术描述&#xff1b;代码示例为交流学习用途&#xff0c;部分参考开源文档&#xff08;Apache 2.0/GPLv3&#xff09;&#xff1b;案例数据已脱敏&#xff0c;技术推荐保持…...

SpringSecurity——前后端分离登录认证

SpringSecurity——前后端分离登录认证的整个过程 前端&#xff1a; 使用Axios向后端发送请求 <!DOCTYPE html> <html lang"en"> <head><meta charset"UTF-8"><title>登录</title><script src"https://cdn…...

【AI】Orin Nano+ubuntu22.04上移植YoloV11,并使用DeepStream测试成功

1、准备工作 使用 sdk-manager 烧写 OrinNano, JetPack版本为6.0 DP,对应操作系统为:Ubuntu22.04 参见博客:【NVIDIA】Jetson Orin Nano系列:烧写Ubuntu22.04 2、安装 PyTorch 2.1 下载依赖 1)安装onnx pip install onnx -i https://pypi.tuna.tsinghua.edu.cn/sim…...

RHCE 使用nginx搭建网站

一。准备工作 Windows dns映射 创建目录网页 vim 编辑内容 添加如下 重启nginx服务&#xff0c;在Windows浏览器进行测试...

arm linux下的读写信号量rw_semphore的实现

本文基于arm linux 5.10来介绍内核中使用的读写信号量rw remphore的实现代码。 内核中信号量结构体struct rw_semaphore的定义在include/linux/rwsem.h 32位architectures下&#xff0c;结构体struct rw_semaphore中的count的使用如下&#xff1a; 先来看信号量的定义和初始化…...

搭建主从DNS、nfs、nginx

任务需求&#xff1a; 客户端通过访问 www.nihao.com 后&#xff0c;能够通过 dns 域名解析&#xff0c;访问到 nginx 服务中由 nfs 共享的首页文件&#xff0c;内容为&#xff1a;Very good, you have successfully set up the system. 各个主机能够实现时间同步&#xff0c;…...

Qt6+QML实现Windows屏幕录制

前言 Qt6提供了更丰富的多媒体支持类&#xff0c;使用Qt6 QMediaCaptureSession、QScreenCapture、QMediaRecorder&#xff0c;来实现一个屏幕录制的demo&#xff0c;其中QScreenCapture 最低版本 Qt6.5。支持录制的清晰度设置&#xff0c;选择视频保存位置&#xff0c;UI使用…...

python二级每日十题(1)

\ 第一题,在Python中&#xff0c;变量名的命名规则&#xff1a;以字母或下划线开头&#xff0c;后面跟字母、下划线和数字&#xff1b;不能以数字开头.故选c项,博主正确 缩进&#xff1a;在逻辑行首的空白&#xff08;空格和制表符&#xff09;用来决定逻辑行的缩进层次&…...

记录一次truncate导致MySQL夯住的故障

目录 环境信息&#xff1a; 故障描述&#xff1a; 处理过程&#xff1a; 原理分析&#xff1a; show processlist结果中的system lock含义&#xff1a; truncate原理&#xff1a; 1. TRUNCATE 的执行流程 2、TRUNCATE 表导致数据库夯住的原因 3、 TRUNCATE 表导致…...

Java Web应用程序实现用户登录、学生信息管理和验证码验证以及页面跳转等基本功能(IDEA)含(Ajax、JSTL)

一、具体框架以及代码功能的展示&#xff1a; 1. 文件结构 web03: 项目根目录。 src: 包含Java源代码。 cn.lvb: 主包。 bean: 包含实体类&#xff0c;如 Book 和 Student。 controller: 包含处理HTTP请求的Servlet类&#xff0c;如 DoLogin, Index, StuList1, VerifyCode。 …...

【Mybatis-plus】在mybatis-plus中 if test标签如何判断 list不为空

博主介绍&#xff1a;✌全网粉丝22W&#xff0c;CSDN博客专家、Java领域优质创作者&#xff0c;掘金/华为云/阿里云/InfoQ等平台优质作者、专注于Java技术领域✌ 技术范围&#xff1a;SpringBoot、SpringCloud、Vue、SSM、HTML、Nodejs、Python、MySQL、PostgreSQL、大数据、物…...

MySQL 事务(Transaction)详解

MySQL 事务&#xff08;Transaction&#xff09;详解 1. 什么是事务&#xff1f; 事务&#xff08;Transaction&#xff09;是一组要么全部执行&#xff0c;要么全部回滚的 SQL 语句&#xff0c;用于保证数据一致性。事务一般用于银行转账、订单支付等操作&#xff0c;确保多个…...

Redis 知识点梳理

第一章 NoSQL数据库发展历史简介 1、 Web的历史发展历程 web1.0时代简介 web 1.0是以编辑为特征&#xff0c;网站提供给用户的内容是网站编辑进行编辑处理后提供的&#xff0c;用户阅读网站提供的内容这个过程是网站到用户的单向行为web1.0时代的代表站点为新浪&#xff0c;…...

github上传操作简单说明

前期准备 0.下载git&#xff08;如果已经有了就不用了&#xff09; 1.在GitHub上新建一个存储库 2.先在本地创建一个目录作为本地库目录&#xff0c;在目录里打开git bash进行上传 上传过程 echo "# Garbled_repair" >> README.md 作用&#xff1a;创建一个…...

在 ASP .NET Core 9.0 中使用 Scalar 创建漂亮的 API 文档

示例代码&#xff1a;https://download.csdn.net/download/hefeng_aspnet/90407900 Scalar 是一款可帮助我们为 API 创建精美文档的工具。与感觉有些过时的默认 Swagger 文档不同&#xff0c;Scalar 为 API 文档提供了全新而现代的 UI。其简洁的设计让开发人员可以轻松找到测试…...

针对 pdf.mjs 文件因 MIME 类型错误导致的 Failed to load module script 问题解决方案

Failed to load module script: Expected a JavaScript module script but the server responded with a MIME type of “application/octet-stream”. Strict MIME type checking is enforced for module scripts per HTML spec. pdf.mjs 这种问题该如何处理 nginx 针对 pdf.…...

Flink介绍与安装

Apache Flink是一个在有界数据流和无界数据流上进行有状态计算分布式处理引擎和框架。Flink 设计旨在所有常见的集群环境中运行&#xff0c;以任意规模和内存级速度执行计算。 一、主要特点和功能 1. 实时流处理: 低延迟: Flink 能够以亚秒级的延迟处理数据流&#xff0c;非常…...

《双指针算法指南:LeetCode 经典题解(C++实现)》

《双指针算法指南&#xff1a;LeetCode 经典题解&#xff08;C实现&#xff09;》 —— 从快慢指针到对撞指针&#xff0c;刷题效率提升 200%&#xff01; 常⻅的双指针有两种形式&#xff0c;⼀种是对撞指针&#xff0c;⼀种是左右指针。 对撞指针&#xff1a; ⼀般⽤于顺…...

kaggle上经典泰坦尼克项目数据分析探索

之前了解在kaggle上这个项目很火&#xff0c;最近想要加强一下python数据分析&#xff0c;所以在kaggle上找到这个项目进行学习探索&#xff0c;下面是将一些学习资料以及过程整理出来。 一、首先我们了解一下项目背景以及如何找到这个项目。 kaggle项目地址: https://www.k…...

【深度学习】多目标融合算法(五):定制门控网络CGC(Customized Gate Control)

目录 一、引言 二、CGC&#xff08;Customized Gate Control&#xff0c;定制门控网络&#xff09; 2.1 技术原理 2.2 技术优缺点 2.3 业务代码实践 2.3.1 业务场景与建模 2.3.2 模型代码实现 2.3.3 模型训练与推理测试 2.3.4 打印模型结构 三、总结 一、引言 上一…...

Ubuntu上查看GPU使用情况并释放内存

先用nvidia-smi查看GPU当前使用情况 再用fuser 命令查找对应显卡上占用 GPU 的进程 最后查到了用kill -9强制杀掉进程&#xff08;PID&#xff09;即可...

大数据学习栈记——HBase安装

本文介绍大数据技术中流行的非关系型数据库HBase的安装&#xff0c;操作系统&#xff1a;Ubuntu24.04 安装Zookeeper 安装HBase前需要先安装Zookeeper&#xff0c;HBase使用Zookeeper作为其分布式协同服务&#xff0c;存储了HBase集群的元数据信息&#xff0c;并提供了分布式…...

[入门]NUC13配置Ubuntu20.04详细步骤

文章目录 1. 安装Ubuntu20.041.1 制作系统启动盘1.1.1 下载镜像文件1.1.2 配置启动盘 1.2 安装内存条、硬盘1.3 安装系统 2. 网卡驱动配置2.1 关闭安全启动2.2 安装intel官方网卡驱动backport2.2.1 第四步可能会出现问题 2.3 ubuntu官方的驱动2.4 重启 3. 软件安装3.1 录屏软件…...

【实战指南】用MongoDB存储文档和图片等大文件(Java实现)

一、前言 在现代应用开发中,经常需要处理和存储大量的文档、图片等大文件。传统的关系型数据库在处理这类大文件时,往往会面临性能瓶颈、存储成本高等问题。而 MongoDB 作为一款流行的 NoSQL 数据库,提供了 GridFS 规范,能够很好地解决大文件存储的问题。GridFS 可以将大文…...

使用Gitee Go流水线部署个人项目到服务器指南

使用Gitee Go流水线部署个人项目到服务器指南 前言&#xff01;&#xff01;&#xff01; 本文解决的问题&#xff1a; 你有一台ECS服务器&#xff0c;你在上面部署了一个Java服务也就是一个jar&#xff0c;你觉着你每次手动本地打包&#xff0c;上传&#xff0c;在通过命令去…...

使用Three.js渲染器创建炫酷3D场景

引言 在当今数字化的时代&#xff0c;3D图形技术正以其独特的魅力在各个领域掀起波澜。从影视制作到游戏开发&#xff0c;从虚拟现实到网页交互&#xff0c;3D场景以其强烈的视觉冲击力和沉浸式的体验&#xff0c;成为了吸引用户、传达信息的重要手段。而Three.js&#xff0c;…...

Spring Boot 集成 Elasticsearch怎样在不启动es的情况下正常启动服务

解释 在spingboot 集成es客户端后&#xff0c;每当服务启动时&#xff0c;服务默认都会查看es中是否已经创建了对应的索引&#xff0c;如果没有索引则创建。基于上面的规则我们可以通过配置不自动创建索引来达到在没有es服务的情况下正常启动服务。 解决办法 在entity类的Docu…...

明远智睿SD2351核心板:多接口融合,破解边缘计算难题

在边缘计算领域&#xff0c;明讯智睿SD2351核心板凭借丰富的接口资源与异构计算架构&#xff0c;成为工业网关与智能终端的理想选择。硬件配置升级 :处理器:四核Cortex-A35&#xff0c;256KB L2缓存&#xff0c;动态调频降低功耗;存储性能:emMC 5.0 HS400模式读写速度提升40%&a…...

xampp安装教程与配置

一、安装 XAMPP &#xff08;一&#xff09;下载 访问官网&#xff1a;打开浏览器&#xff0c;访问 XAMPP 官方网站&#xff08;https://www.apachefriends.org/zh_cn/index.html&#xff09;。 选择版本&#xff1a;根据你的操作系统&#xff08;Windows、macOS 或 Linux&am…...

设计模式之单例模式(Singleton Pattern)

单例模式&#xff08;Singleton Pattern&#xff09;是一种常用的设计模式&#xff0c;确保一个类只有一个实例&#xff0c;并提供一个全局访问点。单例模式在许多场景中非常有用&#xff0c;例如配置管理、日志记录、线程池等。 ### **1. 单例模式的特点** 1. **唯一实例**&a…...

Androidstudio实现引导页文字动画

文章目录 1. 功能需求2. 代码实现过程1. 编写布局文件2. 实现引导页GuideActivity 3. 总结4. 效果演示5. 关于作者其它项目视频教程介绍 1. 功能需求 1、引导页具有两行文字&#xff08;“疫情在前”和“共克时艰”&#xff09;&#xff0c;和向上的 图标。 2、进入【引导页】…...

【linux】线程概念与控制

引言 当现代CPU的晶体管密度逼近物理极限&#xff0c;多核架构已成为突破性能瓶颈的必由之路。在这个计算密集型任务与异步IO需求并行的时代&#xff0c;多线程编程不再是可选项&#xff0c;而是开发者必须掌握的核心技能。Linux作为承载着全球90%云计算负载的操作系统&#xf…...

Cesium 自定义路径导航材质

cesium 自定义路径导航纹理图片随便更换&#xff0c;UI 提供设计图片即可达到效果&#xff1b; 打开小马的weix 关注下 搜索“技术链” 回复关键词《《路径》》获取原始代码&#xff1b; 拿到就能用轻松解决&#xff01;帮忙点个关注吧&#xff01;...

用 pytorch 从零开始创建大语言模型(五):预训练无标注数据

用 pytorch 从零开始创建大语言模型&#xff08;五&#xff09;&#xff1a;预训练无标注数据 5 预训练无标注数据5.1 评估文本生成模型5.1.1 使用 GPT 生成文本5.1.2 计算文本生成损失5.1.3 计算训练集和验证集的损失 5.2 训练 LLM5.3 解码策略以控制随机性5.3.1 温度缩放&am…...

[AI速读]混合验证方案:如何高效解决RISC-V向量扩展的验证难题

RISC-V作为一种开源指令集架构,近年来在AI和高性能计算领域备受关注。其向量扩展(RVV)为处理并行数据提供了强大的支持,但复杂的异常处理和冒险检测机制也带来了验证上的巨大挑战。本文将结合一篇技术论文,解析一种混合验证方案,帮助开发者更高效地解决RVV的验证问题。 一…...

模型空间、图纸空间、布局(Layout)之间联系——CAD c#二次开发

在 AutoCAD 的二次开发中&#xff0c;**模型空间&#xff08;Model Space&#xff09;**、**图纸空间&#xff08;Paper Space&#xff09;** 和 **布局&#xff08;Layout&#xff09;** 是三个核心概念&#xff0c;它们的关系及开发中的操作逻辑如下&#xff1a; --- 1. 模…...

Linux 日常开发常用命令(解释-全)

帮助类 #查看cd命令的帮助信息 [rootcentos100 ~] help cd # 查看网卡配置信息 [rootcentos100 ~] ifconfig # 检测到目标主机是否连接正常 [rootcentos100 ~] ping IP地址[rootcentos100 ~] ssh [-p port] userremote #远程登录user 是在远程机器上的用户名&#xff0c;如果…...

数据库监控:确保业务连续性和用户体验

在数字化时代&#xff0c;数据库作为企业的数据心脏&#xff0c;其重要性不言而喻。无论是交易系统、客户关系管理系统&#xff0c;还是数据分析平台&#xff0c;都离不开数据库的支撑。然而&#xff0c;数据库的运行状态和性能直接影响着企业的业务连续性和用户体验。因此&…...

Java面试黄金宝典9

1. Redis 持久化机制 Redis 提供了两种主要的持久化机制&#xff1a;RDB&#xff08;Redis Database&#xff09;和 AOF&#xff08;Append Only File&#xff09;&#xff0c;下面对这两种机制进行详细介绍。 RDB&#xff08;Redis Database&#xff09; 原理&#xff1a;RDB …...

【C#】WinForm自定义控件及窗体

前言 WinForm&#xff08;Windows Forms&#xff09;是Microsoft.NET框架中的技术&#xff0c;用于开发Windows桌面应用程序。它提供了一套丰富的控件和组件。通过拖放控件、编写事件处理程序等方式快速构建用户界面。 通过属性窗口定制这些控件的外观和行为。 通过数据绑定&am…...