【Linux】39.一个基础的HTTP Web服务器

news2025/4/18 5:02:09

文章目录

  • 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;         // 请求URL
    std::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. 创建Socket
        listensock_.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-Type
    std::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";
            else
                response_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";  // 设置Cookie
            response_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_;    // 监听socket
    uint16_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:
    // 创建Socket
    void Socket()
    {
        // 创建TCP Socket
        sockfd_ = 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;           // IPv4
        local.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;  // 连接成功
    }

    // 关闭Socket
    void 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.coloradmin.cn/o/2332530.html

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈,一经查实,立即删除!

相关文章

阿里云域名证书自动更新acme.sh

因为阿里云的免费证书只有三个月的有效期&#xff0c;每次更换都比较繁琐&#xff0c;所以找到了 acme.sh&#xff0c;还有一种 certbot 我没有去了解&#xff0c;就直接使用了 acme.sh 来更新证书&#xff0c;acme.sh 的主要特点就是&#xff1a; 支持多种 DNS 服务商自动化续…

大数据Hadoop(MapReduce)

MapReduce概述 MapReduce定义 MapReduce是一个分布式运算程序的编程框架&#xff0c;是用户开发“基于Hadoop的数据分析应用”的核心框架。 MapReduce核心功能是将用户编写的业务逻辑代码和自带默认组件整合成一个完整的分布式运算程序&#xff0c;并发运行在一个Hadoop集群上…

图灵逆向——题十七-字体加密

十七题是一个很经典的字体加密案例&#xff0c;很适合新手入门~ 目录列表 过程分析代码实现 过程分析 打开开发者工具直接看请求&#xff0c;发现它请求的没有加密参数&#xff0c;以为万事大吉的你迫不及待的点击了响应&#xff0c;然后就会发现依托。。。 返回的数据中字体…

系统与网络安全------网络通信原理(5)

资料整理于网络资料、书本资料、AI&#xff0c;仅供个人学习参考。 传输层解析 传输层 传输层的作用 IP层提供点到点的连接传输层提供端到端的连接 端口到端口的连接&#xff08;不同端口号&#xff0c;代表不同的应用程序&#xff09; TCP协议概述 TCP&#xff08;Transm…

minio提供nfs服务

minio提供nfs服务 挂载minio为本地目录配置开机自动挂载方法1: 使用supervisor实现开机自动挂载方法2: 服务单元实现开机自动挂载minio为本地目录---失败调试 配置NFS服务端 挂载minio为本地目录 使用 Minio 作为后端存储&#xff0c;并通过 NFS 为客户端提供访问&#xff0c;…

嵌入式---加速度计

一、基本概念与定义 定义 加速度计&#xff08;Accelerometer&#xff09;是一种测量物体加速度&#xff08;线性加速度或振动加速度&#xff09;的传感器&#xff0c;可检测物体运动状态、振动幅度、倾斜角度等&#xff0c;输出与加速度成比例的电信号&#xff08;模拟或数字信…

swagger + Document

swagger 虽然有了api接口&#xff0c;对于复杂接口返回值说明&#xff0c;文档还是不能少。如果是一个人做的还简单一点&#xff0c;现在都搞前后端分离&#xff0c;谁知道你要取那个值呢

【Git】--- 多人协作实战场景

Welcome to 9ilks Code World (๑•́ ₃ •̀๑) 个人主页: 9ilk (๑•́ ₃ •̀๑) 文章专栏&#xff1a; Git 前面我们学习了Git的所有本地仓库的相关操作:git基本操作,分支理解,版本回退,冲突解决等等。同时我们还理解了远端仓库在开发的作用以及相关操作push…

Higress: 阿里巴巴高性能云原生API网关详解

一、Higress概述 Higress是阿里巴巴开源的一款基于云原生技术构建的高性能API网关&#xff0c;专为Kubernetes和微服务架构设计。它集成了Ingress控制器、微服务网关和API网关功能于一体&#xff0c;支持多种协议和丰富的流量管理能力。 发展历程 Higress 从最初社区的 Isti…

VM——相机拍照失败

1、问题&#xff1a;相机频闪触发&#xff0c;在MVS中正常出图&#xff0c;在VM中出现拍照失败 2、解决&#xff1a; 1、首先排查网络设置&#xff08;巨帧是否设置&#xff09; 2、电脑的所有防火墙是否关闭 3、在MVS中恢复相机的设置参数为默认参数&#xff0c;删除VM中的全…

初识Redis · 简单理解Redis

目录 前言&#xff1a; 分布式系统 开源节流 认识Redis 负载均衡 缓存 微服务 前言&#xff1a; 本文只是作为Redis的一篇杂谈&#xff0c;简单理解一下Redis为什么要存在&#xff0c;以及它能做到和它不能做到的事儿&#xff0c;简单提及一下它对应的优势有什么&#…

自动驾驶时间同步

主要包含两个大的概念&#xff1a;时间系统间的时间同步与传感器数据间的时间同步 1. 时间系统间的时间同步 概念&#xff1a; 自动驾驶域控一般由多个芯片与多种类型的传感器组成&#xff0c;如&#xff1a;MCU SoC Camera Lidar Radar USS GNSS&#xff0c;其中 MCU…

项目进度延误的十大原因及应对方案

项目进度延误主要源于以下十大原因&#xff1a;目标不明确、需求频繁变更、资源配置不足或不合理、沟通不畅、风险管理不足、缺乏有效的项目监控、技术难题未及时解决、团队协作效率低下、决策链过长、外部因素影响。其中&#xff0c;需求频繁变更是导致延误的关键因素之一&…

消息队列(IPC技术)

目录 一、Linux 中主要的进程间通信方式如下&#xff1a; 二、消息队列函数 &#xff08;1&#xff09;msgget函数 功能概述 函数原型 参数解释 返回值 示例 结果 问题 (2) msgsnd函数 功能概述 函数原型 参数说明 返回值 示例 结果 &#xff08;3&#xff0…

突破焊丝虚影干扰,端子焊点缺陷检测如何实现自动化?

端子焊点作为 3C 产品中连接电路的关键环节&#xff0c;其质量优劣对产品性能有着决定性影响。然而&#xff0c;传统人工检测端子焊点不仅效率低下&#xff0c;难以满足大规模生产需求&#xff0c;而且误判率高&#xff0c;无法精准把控产品质量&#xff0c;成为企业提质增效智…

电能质量在线监测分析装置支持实时监测、数据存储及远程传输,适用于电网、工业等场景

电能质量在线监测分析装置主要技术指标 2.1工作电源 交流&#xff1a;220V10% &#xff1b;50Hz0.5Hz&#xff1b;谐波畸变率不大于15&#xff05; 直流&#xff1a;220V10%&#xff0c;纹波系数不大于5% 2.2电流信号输入 输入方式&#xff1a;电流互感器输入&#xff1b; …

01分数规划

好久没发博客了……浅浅复活一下&#xff0c;讲个冷门些的算法。 算法目的&#xff1a;选出k组ai,bi使得 最大。 算法过程&#xff1a; 不妨考虑二分答案&#xff0c;那么答案的形式便是 的形式&#xff0c;则可通过移项转化为&#xff0c;进一步的&#xff0c;我们可以将…

网络安全防护技术

边界安全防护——防火墙 控制&#xff1a;在网络连接点上建立一个安全控制点&#xff0c;对进出数据进行限制隔离&#xff1a;将需要保护的网络与不可信任网络进行隔离&#xff0c;隐藏信息并进行安全防护记录&#xff1a;对进出数据进行检查&#xff0c;记录相关信息 防火墙…

课程分享 | 安全设计原则

讲师介绍 前言 在数字化时代&#xff0c;软件安全已从技术问题升级为关乎企业存亡的战略要务。从SolarWinds供应链攻击到Log4j漏洞风暴&#xff0c;一次次安全事件不断警示我们&#xff1a;传统的边界防护思维已无法应对日益复杂的威胁环境。面对不断演进的攻击手段&#xff0…

【数据结构 · 初阶】- 单链表

目录 一.相关指针知识点 二.链表 1.为什么学了顺序表还要学链表 2.优点 三.实现 1.链表的打印 —— 理解链表结构 (2) 物理结构图 2.链表的尾插 —— 入门 错误写法&#xff1a;tail ! NULL 总结&#xff1a; 正确代码物理图解&#xff1a; (2) 尾插整体代码 (思考…