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

【Linux】37.网络版本计算器

文章目录

    • 1. Log.hpp-日志记录器
    • 2. Daemon.hpp-守护进程工具
    • 3. Protocol.hpp-通信协议解析器
    • 4. ServerCal.hpp-计算器服务处理器
    • 5. Socket.hpp-Socket通信封装类
    • 6. TcpServer.hpp-TCP服务器框架
    • 7. ClientCal.cc-计算器客户端
    • 8. ServerCal.cc-计算器服务器
    • 9. 代码时序
      • 1. 服务器启动时序
      • 2. 客户端连接时序
      • 3. 请求处理时序
      • 4. 完整的请求-响应时序
      • 5. 数据处理时序
      • 6. 日志记录时序
      • 7. 资源释放时序


1. Log.hpp-日志记录器

Log.hpp

// 1. 头文件和宏定义
#pragma once  // 防止头文件重复包含// 系统头文件
#include <iostream>    // 标准输入输出
#include <time.h>      // 时间相关函数
#include <stdarg.h>    // 可变参数函数
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h>  // 文件状态
#include <fcntl.h>     // 文件控制
#include <unistd.h>    // POSIX系统调用
#include <stdlib.h>    // 标准库函数// 缓冲区大小
#define SIZE 1024// 日志级别定义
#define Info 0      // 普通信息
#define Debug 1     // 调试信息
#define Warning 2   // 警告信息
#define Error 3     // 错误信息
#define Fatal 4     // 致命错误// 日志输出方式
#define Screen 1     // 输出到屏幕
#define Onefile 2    // 输出到单个文件
#define Classfile 3  // 根据日志级别输出到不同文件// 默认日志文件名
#define LogFile "log.txt"// 2. 日志类定义
class Log {
private:int printMethod;      // 日志输出方式std::string path;     // 日志文件路径public:// 2.1 构造函数:设置默认输出方式Log() {printMethod = Screen;  // 默认输出到屏幕path = "./log/";       // 默认日志目录}// 2.2 设置日志输出方式void Enable(int method) {printMethod = method;}// 2.3 日志级别转字符串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";}}// 2.4 日志输出函数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;}}// 2.5 输出到单个文件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);}// 2.6 根据日志级别输出到不同文件void printClassFile(int level, const std::string &logtxt) {std::string filename = LogFile;filename += ".";filename += levelToString(level); // 例如: "log.txt.Debug"printOneFile(filename, logtxt);}// 2.7 重载函数调用运算符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);}
};// 3. 创建全局日志对象
Log lg;

2. Daemon.hpp-守护进程工具

Daemon.hpp

将进程转换为守护进程的工具类

#pragma once  // 防止头文件重复包含#include <iostream>    // 标准输入输出
#include <cstdlib>     // exit()函数
#include <unistd.h>    // fork(), setsid(), chdir()等系统调用
#include <signal.h>    // 信号处理
#include <string>      // 字符串类
#include <sys/types.h> // 基本系统数据类型
#include <sys/stat.h>  // 文件状态
#include <fcntl.h>     // 文件控制选项// 定义空设备文件路径
const std::string nullfile = "/dev/null";// 守护进程化函数,参数cwd为工作目录
void Daemon(const std::string &cwd = "")
{// 1. 忽略一些可能的干扰信号signal(SIGCLD, SIG_IGN);  // 忽略子进程状态改变信号signal(SIGPIPE, SIG_IGN); // 忽略管道破裂信号signal(SIGSTOP, SIG_IGN); // 忽略停止进程信号// 2. 创建守护进程if (fork() > 0)  // 父进程退出exit(0);setsid();        // 创建新会话,使进程成为会话组长// 3. 改变工作目录if (!cwd.empty())              // 如果指定了工作目录chdir(cwd.c_str());        // 切换到指定目录// 4. 重定向标准输入输出到/dev/nullint fd = open(nullfile.c_str(), O_RDWR); // 以读写方式打开/dev/nullif(fd > 0){dup2(fd, 0);  // 重定向标准输入dup2(fd, 1);  // 重定向标准输出dup2(fd, 2);  // 重定向标准错误close(fd);    // 关闭文件描述符}
}

3. Protocol.hpp-通信协议解析器

Protocol.hpp

定义客户端服务器间通信协议,处理消息的序列化和反序列化

#pragma once#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>  // JSON序列化支持// #define MySelf 1  // 自定义协议开关// 定义分隔符
const std::string blank_space_sep = " ";  // 空格分隔符
const std::string protocol_sep = "\n";    // 协议分隔符// 协议编码函数:将内容封装成格式化的报文
std::string Encode(std::string &content)
{std::string package = std::to_string(content.size());  // 内容长度package += protocol_sep;   // 添加分隔符package += content;        // 添加内容package += protocol_sep;   // 添加分隔符return package;
}// 协议解码函数:从报文中提取内容
// 格式:"len"\n"x op y"\nXXXXXX
bool Decode(std::string &package, std::string *content)
{// 查找第一个分隔符位置std::size_t pos = package.find(protocol_sep);if(pos == std::string::npos) return false;// 获取长度字符串并转换std::string len_str = package.substr(0, pos);std::size_t len = std::stoi(len_str);// 计算完整报文长度std::size_t total_len = len_str.size() + len + 2;if(package.size() < total_len) return false;// 提取内容*content = package.substr(pos+1, len);// 移除已处理的报文package.erase(0, total_len);return true;
}// 请求类:处理计算请求
class Request
{
public:// 构造函数Request(int data1, int data2, char oper) : x(data1), y(data2), op(oper) {}Request() {}public:// 序列化:将请求对象转换为字符串bool Serialize(std::string *out){
#ifdef MySelf// 自定义协议格式:"x op y"std::string s = std::to_string(x);s += blank_space_sep;s += op;s += blank_space_sep;s += std::to_string(y);*out = s;
#else// JSON格式Json::Value root;root["x"] = x;root["y"] = y;root["op"] = op;Json::StyledWriter w;*out = w.write(root);
#endifreturn true;}// 反序列化:将字符串解析为请求对象bool Deserialize(const std::string &in){
#ifdef MySelf// 解析自定义协议格式std::size_t left = in.find(blank_space_sep);if (left == std::string::npos) return false;std::string part_x = in.substr(0, left);std::size_t right = in.rfind(blank_space_sep);if (right == std::string::npos) return false;std::string part_y = in.substr(right + 1);if (left + 2 != right) return false;op = in[left + 1];x = std::stoi(part_x);y = std::stoi(part_y);
#else// 解析JSON格式Json::Value root;Json::Reader r;r.parse(in, root);x = root["x"].asInt();y = root["y"].asInt();op = root["op"].asInt();
#endifreturn true;}void DebugPrint(){std::cout << "新请求构建完成: " << x << op << y << "=?" << std::endl;}public:int x;      // 第一个操作数int y;      // 第二个操作数char op;    // 运算符
};// 响应类:处理计算响应
class Response
{// [响应类的实现与Request类似,只是处理result和code两个字段]// result: 计算结果// code: 状态码,0表示成功,非0表示各种错误
};

4. ServerCal.hpp-计算器服务处理器

ServerCal.hpp

实现服务器端的核心计算逻辑

#pragma once
#include <iostream>
#include "Protocol.hpp"// 定义错误码枚举
enum
{Div_Zero = 1,    // 除零错误Mod_Zero,        // 取模零错误Other_Oper       // 未知运算符错误
};// 服务器端计算器类
class ServerCal
{
public:ServerCal() {}// 核心计算功能辅助函数Response CalculatorHelper(const Request &req){Response resp(0, 0);  // 初始化响应对象,默认结果0,状态码0// 根据运算符进行相应计算switch (req.op){case '+':    // 加法运算resp.result = req.x + req.y;break;case '-':    // 减法运算resp.result = req.x - req.y;break;case '*':    // 乘法运算resp.result = req.x * req.y;break;case '/':    // 除法运算{if (req.y == 0)   // 处理除零错误resp.code = Div_Zero;elseresp.result = req.x / req.y;}break;case '%':    // 取模运算{if (req.y == 0)   // 处理取模零错误resp.code = Mod_Zero;elseresp.result = req.x % req.y;}break;default:     // 未知运算符resp.code = Other_Oper;break;}return resp;}// 主计算函数:处理完整的请求-响应流程// 输入格式示例:"len"\n"10 + 20"\nstd::string Calculator(std::string &package){// 1. 解码请求包std::string content;bool r = Decode(package, &content);  // 解析出实际内容if (!r)return "";// 2. 反序列化请求内容Request req;r = req.Deserialize(content);  // 将内容转换为请求对象if (!r)return "";// 3. 执行计算content = "";  // 清空content准备存储响应Response resp = CalculatorHelper(req);  // 调用计算辅助函数// 4. 构建响应包resp.Serialize(&content);   // 序列化响应对象content = Encode(content);  // 编码响应内容return content;  // 返回完整的响应包}~ServerCal() {}
};

5. Socket.hpp-Socket通信封装类

Socket.hpp

封装底层Socket网络通信功能

#pragma once#include <iostream>
#include <string>
#include <unistd.h>     // Unix标准函数
#include <cstring>      // memset等字符串操作
#include <sys/types.h>  // 基本系统数据类型
#include <sys/stat.h>   // 文件状态
#include <sys/socket.h> // Socket接口
#include <arpa/inet.h>  // IP地址转换函数
#include <netinet/in.h> // IPv4地址结构
#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);}}// 绑定端口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_);}// 获取文件描述符int Fd(){return sockfd_;}private:int sockfd_;  // Socket文件描述符
};

6. TcpServer.hpp-TCP服务器框架

TcpServer.hpp

实现TCP服务器的主框架

#pragma once
#include <functional>
#include <string>
#include <signal.h>
#include "Log.hpp"
#include "Socket.hpp"// 定义回调函数类型:接收字符串参数,返回字符串
using func_t = std::function<std::string(std::string &package)>;// TCP服务器类
class TcpServer
{
public:// 构造函数:初始化端口和回调函数TcpServer(uint16_t port, func_t callback) : port_(port), callback_(callback){}// 初始化服务器bool InitServer(){listensock_.Socket();      // 创建Socketlistensock_.Bind(port_);   // 绑定端口listensock_.Listen();      // 开始监听lg(Info, "init server .... done");return true;}// 启动服务器void Start(){// 忽略子进程退出和管道破裂信号signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);// 主循环while (true){// 接受新的客户端连接std::string clientip;uint16_t clientport;int sockfd = listensock_.Accept(&clientip, &clientport);if (sockfd < 0)continue;// 记录新连接信息lg(Info, "accept a new link, sockfd: %d, clientip: %s, clientport: %d", sockfd, clientip.c_str(), clientport);// 创建子进程处理客户端请求if (fork() == 0){listensock_.Close();  // 子进程关闭监听socketstd::string inbuffer_stream;  // 输入缓冲区// 处理客户端数据while (true){// 读取客户端数据char buffer[1280];ssize_t n = read(sockfd, buffer, sizeof(buffer));if (n > 0)  // 读取成功{buffer[n] = 0;  // 字符串结束符inbuffer_stream += buffer;  // 追加到输入缓冲区// 调试日志lg(Debug, "debug:\n%s", inbuffer_stream.c_str());// 处理所有完整的请求while (true){// 调用回调函数处理请求std::string info = callback_(inbuffer_stream);if (info.empty())  // 没有完整请求则退出循环break;// 调试日志lg(Debug, "debug, response:\n%s", info.c_str());lg(Debug, "debug:\n%s", inbuffer_stream.c_str());// 发送响应给客户端write(sockfd, info.c_str(), info.size());}}else if (n == 0)  // 客户端关闭连接break;else  // 读取错误break;}exit(0);  // 子进程退出}close(sockfd);  // 父进程关闭客户端socket}}~TcpServer(){}private:uint16_t port_;      // 服务器端口Sock listensock_;    // 监听socketfunc_t callback_;    // 处理请求的回调函数
};

7. ClientCal.cc-计算器客户端

ClientCal.cc

实现客户端程序,发送计算请求

#include <iostream>
#include <string>
#include <ctime>
#include <cassert>
#include <unistd.h>
#include "Socket.hpp"
#include "Protocol.hpp"// 打印使用方法
static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << " serverip serverport\n"<< std::endl;
}// 客户端主程序:./clientcal ip port
int main(int argc, char *argv[])
{// 检查命令行参数if (argc != 3){Usage(argv[0]);exit(0);}// 获取服务器IP和端口std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);// 创建并连接SocketSock sockfd;sockfd.Socket();bool r = sockfd.Connect(serverip, serverport);if(!r) return 1;// 初始化随机数种子(使用时间和进程ID)srand(time(nullptr) ^ getpid());int cnt = 1;// 定义可用的运算符const std::string opers = "+-*/%=-=&^";// 输入缓冲区std::string inbuffer_stream;// 进行10次测试while(cnt <= 10){std::cout << "===============第" << cnt << "次测试....., " << "===============" << std::endl;// 随机生成测试数据int x = rand() % 100 + 1;usleep(1234);  // 微秒级延迟int y = rand() % 100;usleep(4321);char oper = opers[rand()%opers.size()];  // 随机选择运算符// 创建请求对象Request req(x, y, oper);req.DebugPrint();  // 打印请求信息// 序列化请求std::string package;req.Serialize(&package);// 编码请求包package = Encode(package);// 发送请求到服务器write(sockfd.Fd(), package.c_str(), package.size());// 读取服务器响应char buffer[128];ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));if(n > 0){buffer[n] = 0;  // 字符串结束符inbuffer_stream += buffer;  // 追加到输入缓冲区std::cout << inbuffer_stream << std::endl;// 解码响应std::string content;bool r = Decode(inbuffer_stream, &content);assert(r);  // 确保解码成功// 反序列化响应Response resp;r = resp.Deserialize(content);assert(r);  // 确保反序列化成功// 打印响应结果resp.DebugPrint();}std::cout << "=================================================" << std::endl;sleep(1);  // 延时1秒cnt++;}// 关闭连接sockfd.Close();return 0;
}

8. ServerCal.cc-计算器服务器

ServerCal.cc

实现服务器程序,处理客户端请求

#include "TcpServer.hpp"
#include "ServerCal.hpp"
#include <unistd.h>
// #include "Daemon.hpp"// 打印使用方法
static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << " port\n" << std::endl; 
}// 服务器主程序:./servercal 8080
int main(int argc, char *argv[])
{// 检查命令行参数if(argc != 2){Usage(argv[0]);exit(0);}// 获取端口号uint16_t port = std::stoi(argv[1]);// 创建计算器服务对象ServerCal cal;// 创建TCP服务器对象// 使用std::bind绑定Calculator方法作为回调函数TcpServer *tsvp = new TcpServer(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));// 初始化服务器tsvp->InitServer();// 将进程变成守护进程// Daemon();  // 自定义守护进程函数daemon(0, 0); // 系统提供的守护进程函数// 第一个参数0:切换工作目录到根目录// 第二个参数0:关闭标准输入输出和错误流// 启动服务器tsvp->Start();/* 以下是测试代码,已注释// 测试响应序列化和反序列化Response resp(1000, 0);std::string content;resp.Serialize(&content);std::cout << content << std::endl;std::string package = Encode(content);std::cout << package;content = "";bool r = Decode(package, &content);std::cout << content << std::endl;Response temp;temp.Deserialize(content);std::cout << temp.result << std::endl;std::cout << temp.code << std::endl;// 测试请求序列化和反序列化Request req(12364566, 43454356, '+');std::string s;req.Serialize(&s);s = Encode(s);std::cout << s;std::string content;bool r = Decode(s, &content);std::cout << content << std::endl;Request temp;temp.Deserialize(content);std::cout << temp.x << std::endl;std::cout << temp.op << std::endl;std::cout << temp.y << std::endl;*/return 0;
}

9. 代码时序

1. 服务器启动时序

ServerCal.cc (主程序)↓
1. 解析命令行参数(端口号)↓
2. 创建ServerCal对象↓
3. 创建TcpServer对象|→ 绑定Calculator回调函数↓
4. 初始化服务器(InitServer)|→ 创建Socket|→ 绑定端口|→ 开始监听↓
5. 守护进程化|→ 后台运行|→ 重定向标准IO↓
6. 启动服务器(Start)|→ 注册信号处理|→ 进入主循环

2. 客户端连接时序

TcpServer::Start (主循环)↓
1. Accept等待连接↓
2. 收到新连接|→ 获取客户端信息(IP/端口)|→ 记录连接日志↓
3. Fork子进程|→ 子进程:处理客户端请求|→ 父进程:继续Accept新连接

3. 请求处理时序

子进程处理流程↓
1. 读取客户端数据|→ 追加到输入缓冲区↓
2. 解析协议(Protocol::Decode)|→ 提取消息长度|→ 检查完整性↓
3. 调用回调函数(Calculator)|→ 反序列化请求|→ 执行计算|→ 序列化响应↓
4. 发送响应|→ 编码响应包|→ 写入socket

4. 完整的请求-响应时序

客户端                    服务器                    子进程|                        |                         ||------ 连接请求 ------>|                         ||                        |--- fork() ------------->||                        |                         ||------ 计算请求 ----------------------→          ||                        |                         ||                        |        1. 解析请求      ||                        |        2. 执行计算      ||                        |        3. 构造响应      ||                        |                         ||<----- 计算结果 ----------------------           ||                        |                         ||------ 关闭连接 ------>|                         ||                        |                         |

5. 数据处理时序

Request数据流↓
1. 序列化(Serialize)|→ JSON格式或自定义格式↓
2. 协议封装(Encode)|→ 添加长度和分隔符↓
3. 网络传输|→ write/read↓
4. 协议解析(Decode)|→ 提取有效载荷↓
5. 反序列化(Deserialize)|→ 还原对象数据

6. 日志记录时序

Log系统↓
1. 生成日志内容|→ 时间戳|→ 日志级别|→ 具体信息↓
2. 根据配置输出|→ 屏幕显示|→ 单一文件|→ 分级文件

7. 资源释放时序

程序退出流程↓
1. 子进程退出|→ 关闭客户端socket|→ exit(0)↓
2. 父进程清理|→ SIGCHLD信号处理|→ 僵尸进程回收

这种时序设计的优点:

  1. 多进程并发处理请求
  2. 父子进程职责明确
  3. 协议设计清晰
  4. 资源管理完善
  5. 错误处理周到

主要的时序特点是采用了经典的多进程并发服务器模型,每个客户端连接由独立的子进程处理,保证了请求处理的隔离性和可靠性。

相关文章:

【Linux】37.网络版本计算器

文章目录 1. Log.hpp-日志记录器2. Daemon.hpp-守护进程工具3. Protocol.hpp-通信协议解析器4. ServerCal.hpp-计算器服务处理器5. Socket.hpp-Socket通信封装类6. TcpServer.hpp-TCP服务器框架7. ClientCal.cc-计算器客户端8. ServerCal.cc-计算器服务器9. 代码时序1. 服务器启…...

3.6c语言

#define _CRT_SECURE_NO_WARNINGS #include <math.h> #include <stdio.h> int main() {int sum 0,i,j;for (j 1; j < 1000; j){sum 0;for (i 1; i < j; i){if (j % i 0){sum i;} }if (sum j){printf("%d是完数\n", j);}}return 0; }#de…...

【 IEEE出版 | 快速稳定EI检索 | 往届已EI检索】2025年储能及能源转换国际学术会议(ESEC 2025)

重要信息 主会官网&#xff1a;www.net-lc.net 【论文【】投稿】 会议时间&#xff1a;2025年5月9-11日 会议地点&#xff1a;中国-杭州 截稿时间&#xff1a;见官网 提交检索&#xff1a;IEEE Xplore, EI Compendex, Scopus 主会NET-LC 2025已进入IEEE 会议官方列表!&am…...

JVM常用概念之本地内存跟踪

问题 Java应用启动或者运行过程中报“内存不足&#xff01;”&#xff0c;我们该怎么办? 基础知识 对于一个在本地机器运行的JVM应用而言&#xff0c;需要足够的内存来存储机器代码、堆元数据、类元数据、内存分析等数据结构&#xff0c;来保证JVM应用的成功启动以及未来平…...

JVM 的主要组成部分及其作用?

创作内容丰富的干货文章很费心力&#xff0c;感谢点过此文章的读者&#xff0c;点一个关注鼓励一下作者&#xff0c;激励他分享更多的精彩好文&#xff0c;谢谢大家&#xff01; JVM包含两个子系统和两个组件&#xff0c;两个子系统为Class loader(类装载)、Execution engine(执…...

从能耗监测到碳资产管理:智慧校园能源管理平台的迭代升级与实践启示

一、核心价值提炼 随着我国能源结构转型的持续优化和“双碳”目标的明确&#xff0c;构建现代化能源体系已成为国家发展的重要战略。在这一背景下&#xff0c;校园作为能源消耗的重要领域&#xff0c;其能源管理的智能化、绿色化转型显得尤为重要。本文将深入探讨校园智慧能源…...

数据库核心-redo、undo

一、redo日志 InnoDB操作以页为单位操作数据。并且首先操作内存中缓冲池的数据&#xff0c;然后刷新到disk中&#xff0c;但如果事务提交后宕机、未能刷新到disk中&#xff0c;就会造成不一致情况。 重做日志&#xff1a; 系统重启时按照修改步骤重新更新数据页 redo日志占用…...

Ubuntu 下 nginx-1.24.0 源码分析 - ngx_core_module

定义在 src\core\nginx.c ngx_module_t ngx_core_module {NGX_MODULE_V1,&ngx_core_module_ctx, /* module context */ngx_core_commands, /* module directives */NGX_CORE_MODULE, /* module type */NULL…...

SQLAlchemy系列教程:如何执行原生SQL

Python中的数据库交互提供了高级API。但是&#xff0c;有时您可能需要执行原始SQL以提高效率或利用数据库特定的特性。本指南介绍在SQLAlchemy框架内执行原始SQL。 在SQLAlchemy中执行原生SQL SQLAlchemy虽然以其对象-关系映射&#xff08;ORM&#xff09;功能而闻名&#xff…...

怎么实现: 大语言模型微调案例

怎么实现: 大语言模型微调案例 目录 怎么实现: 大语言模型微调案例输入一个反常识的问题:首都在北京天安门之后对输出模型进行测试:首都在北京天安门微调代码:测试微调模型代码:微调输出模型结构输出模型参数大小对比Qwen 2.5_0.5:53MB输出模型:951MB 是一样的,没有进行…...

【Linux内核系列】:深入理解缓冲区

&#x1f525; 本文专栏&#xff1a;Linux &#x1f338;作者主页&#xff1a;努力努力再努力wz ★★★ 本文前置知识&#xff1a; 文件系统以及相关系统调用接口 输入以及输出重定向 那么在此前的学习中&#xff0c;我们了解了文件的概念以及相关的系统调用接口&#xff0c;并…...

【Qt】成员函数指针

一、成员函数指针的本质 与普通函数指针的区别&#xff1a; // 普通函数指针 void (*funcPtr)() &普通函数;// 成员函数指针 void (MyClass::*memberFuncPtr)() &MyClass::成员函数;• 绑定对象&#xff1a;成员函数指针必须与类的实例对象结合使用 • 隐含 this 指…...

关于 Can Utils 的详细介绍、使用方法及配置指南

Can Utils&#xff1a;开源CAN总线工具集合 一、Can Utils简介 Can Utils 是一组开源的CAN总线工具链&#xff0c;专为嵌入式开发者和网络诊断工程师设计&#xff0c;支持Linux、Windows和macOS系统。它包含一系列轻量级命令行工具&#xff08;如 cantoolz、candump、canbus …...

【Academy】OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities

OAuth 2.0 身份验证漏洞 ------ OAuth 2.0 authentication vulnerabilities 1. 什么是 OAuth&#xff1f;2. OAuth 2.0 是如何工作的&#xff1f;3. OAuth 授权类型3.1 OAuth 范围3.2 授权代码授权类型3.3 隐式授权类型 4. OAuth 身份验证4.1 识别 OAuth 身份验证4.2 侦察OAuth…...

dify中使用NL2SQL

在 Dify 工作流中融入 NL2SQL&#xff08;自然语言转 SQL&#xff09;之能力&#xff0c;可依循如下步骤达成&#xff0c;借由 Dify 的模块化设计以及模型编排之功能&#xff0c;优化数据库查询之智能化交互&#xff1a; 一、环境准备与 Dify 部署 安装 Docker 与 Dify 务须确…...

android viewmodel如何使用

嗯&#xff0c;我现在要学习如何在Android中使用ViewModel。我之前听说过ViewModel是用来管理UI相关数据的&#xff0c;这样在配置变化比如屏幕旋转时数据不会丢失。但具体怎么用呢&#xff1f;我需要先回忆一下相关的知识。 首先&#xff0c;ViewModel应该是Android Architec…...

蓝桥杯备赛-基础训练(四)字符串 day17

好久不见&#xff0c;今天开始继续更新&#xff0c;或许拿不了奖&#xff0c;但是希望记录自己学习的过程&#xff0c;如果感觉有收获的同学在下面多多评论说说我代码的缺陷&#xff0c;感谢大家&#xff01; 1、反转字符串 编写一个函数&#xff0c;其作用是将输入的字符串反…...

AI Agent开发框架分析:前端视角

1. Mastra (https://mastra.ai/docs) 优点&#xff1a; 提供直观的界面构建器&#xff0c;适合无代码/低代码开发支持JavaScript/TypeScript&#xff0c;可直接集成到前端项目可视化工作流设计&#xff0c;降低入门门槛内置多种UI组件&#xff0c;加速前端开发 缺点&#xf…...

第3节:IP地址分类与子网划分实战指南

IP地址分类与子网划分实战指南:从小白到入门 在网络通信中,IP地址是设备之间相互识别和通信的基础。无论是家庭网络还是企业网络,IP地址的分配和管理都是网络运维的核心任务之一。然而,对于初学者来说,IP地址的分类、子网掩码、CIDR(无类别域间路由)和VLSM(可变长子网…...

贪心算法三

> 作者&#xff1a;დ旧言~ > 座右铭&#xff1a;松树千年终是朽&#xff0c;槿花一日自为荣。 > 目标&#xff1a;了解什么是贪心算法&#xff0c;并且掌握贪心算法。 > 毒鸡汤&#xff1a;有些事情&#xff0c;总是不明白&#xff0c;所以我不会坚持。早安! >…...

pytest基础知识

pytest知识了解 pytest的基础知识了解&#xff1a;Python测试框架之pytest详解_lovedingd的博客-CSDN博客_pytest框架 (包含设置断点&#xff0c;pdb&#xff0c;获取最慢的10个用例的执行耗时) pytest-pytest.main()运行测试用例&#xff0c;pytest参数&#xff1a; pytest-…...

JavaWeb后端基础(7)AOP

AOP是Spring框架的核心之一&#xff0c;那什么是AOP&#xff1f;AOP&#xff1a;Aspect Oriented Programming&#xff08;面向切面编程、面向方面编程&#xff09;&#xff0c;其实说白了&#xff0c;面向切面编程就是面向特定方法编程。AOP是一种思想&#xff0c;而在Spring框…...

[AI]从零开始的ComflyUI安装教程

一、前言 AI画图如今已经进化到了让人难以想象的地步。早在2022年各大视频网站上就出现了许多真人使用AI绘制二次元形象的视频。在那个时期&#xff0c;也有人凭借AI画图狠狠的吃到了一波AI红利。在现在&#xff0c;对于普通人来说&#xff0c;AI画图仍然是非常值得探索的。不管…...

文本对抗样本系列的论文阅读笔记(整理合订)

文本对抗样本系列的论文阅读笔记 以前调研文本对抗样本时的论文笔记梳理&#xff0c;论文都很经典&#xff0c;有现成的框架&#xff08;TextAttack&#xff09;可以直接用&#xff0c;论文中部分内容直接是截取自论文&#xff0c;所以存在中英混合笔记的情况。 BERT-Attack …...

鸿基智启:东土科技为具身智能时代构建确定性底座

人类文明的每一次跨越都伴随着工具的革新。从蒸汽机的齿轮到计算机的代码&#xff0c;生产力的进化始终与技术的“具身化”紧密相连。当大语言模型掀起认知革命&#xff0c;具身智能正以“物理实体自主决策”的双重属性重新定义工业、医疗、服务等领域的运行逻辑。在这场革命中…...

javascript-es6 (六)

编程思想 面向过程 面向过程 就是分析出解决问题所需要的步骤&#xff0c;然后用函数把这些步骤一步一步实现&#xff0c;使用的时候再一个一个的依次 调用就可以了 就是按照我们分析好了的步骤&#xff0c;按照步骤解决问题 面向对象 面向对象 是把事务分解成为一个个对象&am…...

【leetcode hot 100 19】删除链表的第N个节点

解法一&#xff1a;将ListNode放入ArrayList中&#xff0c;要删除的元素为num list.size()-n。如果num 0则将头节点删除&#xff1b;否则利用num-1个元素的next删除第num个元素。 /*** Definition for singly-linked list.* public class ListNode {* int val;* Lis…...

微信小程序将markdown内容转为pdf并下载

要在微信小程序中将Markdown内容转换为PDF并下载,您可以使用以下方法: 方法一:使用第三方API服务 选择第三方API服务: 可以选择像 Pandoc、Markdown-PDF 或 PDFShift 这样的服务,将Markdown转换为PDF。例如,PDFShift 提供了一个API接口,可以将Markdown内容转换为PDF格式…...

【贪心算法】将数组和减半的最小操作数

1.题目解析 2208. 将数组和减半的最少操作次数 - 力扣&#xff08;LeetCode&#xff09; 2.讲解算法原理 使用当前数组中最大的数将它减半&#xff0c;&#xff0c;直到数组和减小到一半为止&#xff0c;从而快速达到目的 重点是找到最大数&#xff0c;可以采用大根堆快速达到…...

【面试】Kafka

Kafka 1、为什么要使用 kafka2、Kafka 的架构是怎么样的3、什么是 Kafka 的重平衡机制4、Kafka 几种选举过程5、Kafka 高水位了解过吗6、Kafka 如何保证消息不丢失7、Kafka 如何保证消息不重复消费8、Kafka 为什么这么快 1、为什么要使用 kafka 1. 解耦&#xff1a;在一个复杂…...

PHP MySQL 创建数据库

PHP MySQL 创建数据库 引言 在网站开发中&#xff0c;数据库是存储和管理数据的核心部分。PHP 和 MySQL 是最常用的网页开发语言和数据库管理系统之一。本文将详细介绍如何在 PHP 中使用 MySQL 创建数据库&#xff0c;并对其操作进行详细讲解。 前提条件 在开始创建数据库之…...

通义万相 2.1 × 蓝耘智算:AIGC 界的「黄金搭档」如何重塑创作未来?

我的个人主页 我的专栏&#xff1a; 人工智能领域、java-数据结构、Javase、C语言&#xff0c;希望能帮助到大家&#xff01;&#xff01;&#xff01; 点赞&#x1f44d;收藏❤ 引言 在当今数字化浪潮席卷的时代&#xff0c;AIGC&#xff08;生成式人工智能&#xff09;领域正…...

【面试题系列】:使用消息队列怎么防止消息重复?从原理到实战……

一、消息队列的核心价值与挑战 消息队列&#xff08;MQ&#xff09;作为现代分布式系统的基础设施&#xff0c;其核心价值在于解耦、削峰填谷和异步通信。但在追求高可靠性的过程中&#xff0c;消息重复成为必须攻克的技术难题。根据调研数据&#xff0c;在生产环境中消息重复…...

Damage与Injury

### “Damage”和“Injury”的区别 “Damage”和“Injury”都有“损害”或“伤害”的意思&#xff0c;但它们的用法、语境和侧重点有所不同。以下是从词性、适用对象、语义侧重和具体场景四个方面详细对比两者的区别&#xff1a; --- #### 1. **词性** - **Damage**&#xf…...

18 HarmonyOS NEXT UVList组件开发指南(五)

温馨提示&#xff1a;本篇博客的详细代码已发布到 git : https://gitcode.com/nutpi/HarmonyosNext 可以下载运行哦&#xff01; 第五篇&#xff1a;UVList组件最佳实践与实际应用案例 文章目录 第五篇&#xff1a;UVList组件最佳实践与实际应用案例1. 最佳实践总结1.1 组件设计…...

vue3组合式API怎么获取全局变量globalProperties

设置全局变量 main.ts app.config.globalProperties.$category { index: 0 } 获取全局变量 const { appContext } getCurrentInstance() as ComponentInternalInstance console.log(appContext.config.globalProperties.$category) 或是 const { proxy } getCurrentInstance…...

华为机试牛客刷题之HJ14 字符串排序

HJ14 字符串排序 描述 对于给定的由大小写字母混合构成的 n 个单词&#xff0c;输出按字典序从小到大排序后的结果。 从字符串的第一个字符开始逐个比较&#xff0c;直到找到第一个不同的位置&#xff0c;通过比较这个位置字符对应的&#xff08;A<⋯<Z<a<⋯<…...

CPU 负载 和 CPU利用率 的区别

简单记录下 top 命令中&#xff0c;CPU利用率核CPU负载的概念&#xff0c; &#xff08;1&#xff09;CPU利用率&#xff1a;指在一段时间内 表示 CPU 实际工作时间占总时间的百分比。表示正在执行进程的时间比例&#xff0c;包括用户空间和内核空间程序的执行时间。通常包含以…...

SSM框架

SSM 框架是 Java Web 开发中广泛使用的经典组合&#xff0c;由 Spring、Spring MVC 和 MyBatis 三个开源框架整合而成&#xff0c;适用于构建中大型企业级应用。 1. SSM框架组成 框架作用核心特性Spring管理业务层&#xff08;Service&#xff09;和持久层&#xff08;DAO&am…...

maven无法解析插件 org.apache.maven.plugins:maven-jar-plugin:3.4.1

解决流程 1.修改maven仓库库地址 2.删除本地的maven仓库 maven插件一直加载有问题: 无法解析插件 org.apache.maven.plugins:maven-jar-plugin:3.4.1 开始以为maven版本有问题&#xff0c;重装了maven&#xff0c;重装了idea工具。结果问题还是没解决。研究之后发现&#xf…...

如何修复“RPC 服务器不可用”错误

远程过程调用&#xff08;Remote Procedure Call&#xff0c; RPC&#xff09;是允许客户端在不同计算机上执行进程的众多可用网络进程之一。本文将深入探讨RPC如何在不同的软件系统之间实现无缝消息交换&#xff0c;同时重点介绍与RPC相关的常见错误的一些原因。 什么是远程过…...

晋升系列4:学习方法

每一个成功的人&#xff0c;都是从底层开始打怪&#xff0c;不断的总结经验&#xff0c;一步一步打上来的。在这个过程中需要坚持、总结方法论。 对一件事情长久坚持的人其实比较少&#xff0c;在坚持的人中&#xff0c;不断的总结优化的更少&#xff0c;所以最终达到高级别的…...

单链表-代码精简版

单链表核心知识详解 单链表是一种动态存储的线性数据结构&#xff0c;其特点是逻辑上连续&#xff0c;物理上非连续&#xff0c;每个节点包含数据域和指向下一个节点的指针域。以下是核心知识点与完整实现代码&#xff1a; 一、单链表的结构定义 单链表节点通过结构体自引用…...

关于前后端整合和打包成exe文件的个人的总结和思考

前言 感觉有很多东西&#xff0c;不知道写什么&#xff0c;随便写点吧。 正文 前后端合并 就不说怎么开发的&#xff0c;就说点个人感觉重要的东西。 前端用ReactViteaxios随便写一个demo&#xff0c;用于CRUD。 后端用Django REST Framework。 设置前端打包 import { …...

基于muduo+mysql+jsoncpp的简易HTTPWebServer

一、项目介绍 本项目基于C语言、陈硕老师的muduo网络库、mysql数据库以及jsoncpp&#xff0c;服务器监听两个端口&#xff0c;一个端口用于处理http请求&#xff0c;另一个端口用于处理发送来的json数据。 此项目在实现时&#xff0c;识别出车牌后打包为json数据发送给后端服务…...

Java/Kotlin逆向基础与Smali语法精解

1. 法律警示与道德边界 1.1 司法判例深度剖析 案例一&#xff1a;2021年某游戏外挂团伙刑事案 犯罪手法&#xff1a;逆向《王者荣耀》通信协议&#xff0c;修改战斗数据包 技术细节&#xff1a;Hook libil2cpp.so的SendPacket函数 量刑依据&#xff1a;非法经营罪&#xff…...

C++:入门详解(关于C与C++基本差别)

目录 一.C的第一个程序 二.命名空间&#xff08;namespace&#xff09; 1.命名空间的定义与使用&#xff1a; &#xff08;1&#xff09;命名空间里可以定义变量&#xff0c;函数&#xff0c;结构体等多种类型 &#xff08;2&#xff09;命名空间调用&#xff08;&#xf…...

CI/CD—GitLab钩子触发Jenkins自动构建项目

GitLab钩子简介&#xff1a; 项目钩子 项目钩子是针对单个项目的钩子&#xff0c;会在项目级别的特定事件发生时触发。这些事件包括代码推送、合并请求创建、问题创建等。项目钩子由项目管理员或具有相应权限的用户进行配置&#xff0c;仅对特定项目生效。 使用场景&#xff1a…...

RPA 职业前景:个人职场发展的 “新机遇”

1. RPA职业定义与范畴 1.1 RPA核心概念 机器人流程自动化&#xff08;RPA&#xff09;是一种通过软件机器人模拟人类操作&#xff0c;自动执行重复性、规则性任务的技术。RPA的核心在于其能够高效、准确地处理大量数据和流程&#xff0c;减少人工干预&#xff0c;从而提高工作…...

【CSS3】金丹篇

目录 标准流概念元素类型及排列规则块级元素行内元素行内块元素 标准流的特点打破标准流 浮动基本使用清除浮动额外标签法单伪元素法双伪元素法&#xff08;推荐&#xff09;overflow 法 Flex 布局Flex 组成主轴对齐方式侧轴对齐方式修改主轴方向弹性盒子伸缩比弹性盒子换行行对…...