【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 功能实现:
总体功能:
提供Web服务,响应客户端(浏览器)的HTTP请求
支持静态文件服务(如HTML、图片等)
多线程处理并发请求
带日志记录功能
- 具体工作流程:
浏览器 → 发送HTTP请求 → 服务器↓解析请求↓查找文件↓返回响应↓
浏览器 ← 显示页面 ← 服务器
- 各模块职责:
日志记录器(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;
}
相关文章:
【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 功能实现: 总体功能: 提供We…...
阿里云域名证书自动更新acme.sh
因为阿里云的免费证书只有三个月的有效期,每次更换都比较繁琐,所以找到了 acme.sh,还有一种 certbot 我没有去了解,就直接使用了 acme.sh 来更新证书,acme.sh 的主要特点就是: 支持多种 DNS 服务商自动化续…...
大数据Hadoop(MapReduce)
MapReduce概述 MapReduce定义 MapReduce是一个分布式运算程序的编程框架,是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序,并发运行在一个Hadoop集群上…...
图灵逆向——题十七-字体加密
十七题是一个很经典的字体加密案例,很适合新手入门~ 目录列表 过程分析代码实现 过程分析 打开开发者工具直接看请求,发现它请求的没有加密参数,以为万事大吉的你迫不及待的点击了响应,然后就会发现依托。。。 返回的数据中字体…...
(自用)蓝桥杯准备(需要写的基础)
要写的文件 led_app lcd_app key_app adc_app usart_app scheduler LHF_SYS一、外设引脚配置 1. 按键引脚 按键引脚配置如下: B1:PB0B2:PB1B3:PB2B4:PA0 2. LCD引脚 LCD引脚配置如下: GPIO_Pin_9 /* …...
系统与网络安全------网络通信原理(5)
资料整理于网络资料、书本资料、AI,仅供个人学习参考。 传输层解析 传输层 传输层的作用 IP层提供点到点的连接传输层提供端到端的连接 端口到端口的连接(不同端口号,代表不同的应用程序) TCP协议概述 TCP(Transm…...
minio提供nfs服务
minio提供nfs服务 挂载minio为本地目录配置开机自动挂载方法1: 使用supervisor实现开机自动挂载方法2: 服务单元实现开机自动挂载minio为本地目录---失败调试 配置NFS服务端 挂载minio为本地目录 使用 Minio 作为后端存储,并通过 NFS 为客户端提供访问,…...
vue2添加背景水印-手动实现(无组件模式)
1. App.vue <template><div id="app" class="app"><router-view></router-view></div> </template><script> export default {mounted() {this.updateWatermark();// 监听路由变化this.$router.afterEach(() =…...
嵌入式---加速度计
一、基本概念与定义 定义 加速度计(Accelerometer)是一种测量物体加速度(线性加速度或振动加速度)的传感器,可检测物体运动状态、振动幅度、倾斜角度等,输出与加速度成比例的电信号(模拟或数字信…...
swagger + Document
swagger 虽然有了api接口,对于复杂接口返回值说明,文档还是不能少。如果是一个人做的还简单一点,现在都搞前后端分离,谁知道你要取那个值呢...
【Git】--- 多人协作实战场景
Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏: Git 前面我们学习了Git的所有本地仓库的相关操作:git基本操作,分支理解,版本回退,冲突解决等等。同时我们还理解了远端仓库在开发的作用以及相关操作push…...
Higress: 阿里巴巴高性能云原生API网关详解
一、Higress概述 Higress是阿里巴巴开源的一款基于云原生技术构建的高性能API网关,专为Kubernetes和微服务架构设计。它集成了Ingress控制器、微服务网关和API网关功能于一体,支持多种协议和丰富的流量管理能力。 发展历程 Higress 从最初社区的 Isti…...
常见的 set 选项与空变量检查
在编写 Bash 脚本时,使用 set 命令中的一些选项可以帮助我们在脚本执行过程中及时捕获错误和潜在问题,避免脚本在出错时继续执行,提高脚本的可靠性和健壮性。 set -e:遇到错误就停 set -e 的作用是:一旦脚本中的某个…...
leetcode 377. Combination Sum IV
这道题也是完全背包问题。这道题和第518题几乎一摸一样,所不同的是,第518题要求的是组合数,而第377题要求的是排列数。虽然本题题目描述中说求的是组合数,但从例子1中(1,1,2)和&…...
VM——相机拍照失败
1、问题:相机频闪触发,在MVS中正常出图,在VM中出现拍照失败 2、解决: 1、首先排查网络设置(巨帧是否设置) 2、电脑的所有防火墙是否关闭 3、在MVS中恢复相机的设置参数为默认参数,删除VM中的全…...
初识Redis · 简单理解Redis
目录 前言: 分布式系统 开源节流 认识Redis 负载均衡 缓存 微服务 前言: 本文只是作为Redis的一篇杂谈,简单理解一下Redis为什么要存在,以及它能做到和它不能做到的事儿,简单提及一下它对应的优势有什么&#…...
目标检测YOLO实战应用案例100讲- 基于卷积神经网络的小目标检测算法研究与应用
目录 知识储备 基于改进YOLOv5的小目标检测算法 一、环境配置(Python 3.8+) 二、核心代码实现 1. 改进模型定义(models/yolov5s_tiny.py ) 2. 小目标数据增强(datasets/tiny_aug.py ) 3. 训练脚本(train.py ) 三、关键改进点说明 四、实验配置建议 前言 传统…...
自动驾驶时间同步
主要包含两个大的概念:时间系统间的时间同步与传感器数据间的时间同步 1. 时间系统间的时间同步 概念: 自动驾驶域控一般由多个芯片与多种类型的传感器组成,如:MCU SoC Camera Lidar Radar USS GNSS,其中 MCU…...
项目进度延误的十大原因及应对方案
项目进度延误主要源于以下十大原因:目标不明确、需求频繁变更、资源配置不足或不合理、沟通不畅、风险管理不足、缺乏有效的项目监控、技术难题未及时解决、团队协作效率低下、决策链过长、外部因素影响。其中,需求频繁变更是导致延误的关键因素之一&…...
消息队列(IPC技术)
目录 一、Linux 中主要的进程间通信方式如下: 二、消息队列函数 (1)msgget函数 功能概述 函数原型 参数解释 返回值 示例 结果 问题 (2) msgsnd函数 功能概述 函数原型 参数说明 返回值 示例 结果 (3࿰…...
突破焊丝虚影干扰,端子焊点缺陷检测如何实现自动化?
端子焊点作为 3C 产品中连接电路的关键环节,其质量优劣对产品性能有着决定性影响。然而,传统人工检测端子焊点不仅效率低下,难以满足大规模生产需求,而且误判率高,无法精准把控产品质量,成为企业提质增效智…...
电能质量在线监测分析装置支持实时监测、数据存储及远程传输,适用于电网、工业等场景
电能质量在线监测分析装置主要技术指标 2.1工作电源 交流:220V10% ;50Hz0.5Hz;谐波畸变率不大于15% 直流:220V10%,纹波系数不大于5% 2.2电流信号输入 输入方式:电流互感器输入; …...
Springboot整合JAVAFX
Springboot整合JAVAFX 实体与VO设计 pom.xml文件如下: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0"xmlns:xsi"http://www.w3.org/2001/XMLSchema-instance"xs…...
01分数规划
好久没发博客了……浅浅复活一下,讲个冷门些的算法。 算法目的:选出k组ai,bi使得 最大。 算法过程: 不妨考虑二分答案,那么答案的形式便是 的形式,则可通过移项转化为,进一步的,我们可以将…...
网络安全防护技术
边界安全防护——防火墙 控制:在网络连接点上建立一个安全控制点,对进出数据进行限制隔离:将需要保护的网络与不可信任网络进行隔离,隐藏信息并进行安全防护记录:对进出数据进行检查,记录相关信息 防火墙…...
[数据结构]Trie字典树
GPT的介绍 🧠 一句话总结: 字典树是一种专门用来存很多字符串的“超级前缀树”,查找某个字符串或前缀的时候,特别快! ✍️ 举个生活例子(类比): 你想做一个词典(Dictio…...
【WPF】IOC控制反转的应用:弹窗但不互相调用ViewModel
全称:Inversion of Control,控制反转 场景:A页面需要调用B/C页面等,防止直接在VM中新建别的页面实例,使用IOC设计架构; 创建Service,在Service中实现页面的实例创建和定义页面输入输出参数。 在…...
课程分享 | 安全设计原则
讲师介绍 前言 在数字化时代,软件安全已从技术问题升级为关乎企业存亡的战略要务。从SolarWinds供应链攻击到Log4j漏洞风暴,一次次安全事件不断警示我们:传统的边界防护思维已无法应对日益复杂的威胁环境。面对不断演进的攻击手段࿰…...
【数据结构 · 初阶】- 单链表
目录 一.相关指针知识点 二.链表 1.为什么学了顺序表还要学链表 2.优点 三.实现 1.链表的打印 —— 理解链表结构 (2) 物理结构图 2.链表的尾插 —— 入门 错误写法:tail ! NULL 总结: 正确代码物理图解: (2) 尾插整体代码 (思考…...
在Linux系统命令行如何使用deepseek官方API调用AI大模型?
在Linux系统命令行如何调用deepseek官方API调用AI大模型? 书接上文: 同样的开头哈哈哈哈: ”在这个AI技术飞速发展的时代,每一个程序员都应该问问自己:如何将人工智能的强大能力融入到我们熟悉的操作系统中ÿ…...
