当前位置: 首页 > 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. 服务器启…...

linux安装Mariadb10.5并修改端口

首先配置yum源 进入下方的文件进行配置 vim /etc/yum.repos.d/MariaDB.repo填写下方内容 [mariadb]name MariaDBbaseurl https:///mirrors.aliyun.com/mariadb/yum/10.5/centos8-amd64/gpgkeyhttps:///mirrors.aliyun.com/mariadb/yum/RPM-GPG-KEY-MariaDBmodule_hotfixes…...

从Windows到ARM Linux:Qt程序的交叉编译与移植指南

引言 在嵌入式开发中&#xff0c;我们经常需要将桌面端开发的Qt程序部署到ARM架构的Linux设备。本文详细介绍如何将Windows平台开发的Qt程序&#xff0c;通过Linux虚拟机交叉编译为ARM架构可执行文件的完整过程 环境准备 需要特别注意的是&#xff0c;对于CentOS 7 默认支持…...

【微信小程序】uniapp开发微信小程序

uniapp开发微信小程序 1、上拉加载 下拉刷新 import { onReachBottom, onPullDownRefresh } from dcloudio/uni-app;配置允许下拉刷新&#xff1a; {"path" : "pages/pet/pet","style" : {"navigationBarTitleText" : ""…...

多视图几何--结构恢复--三角测量

三角测量 1. 核心公式推导 假设两个相机的投影矩阵为 P P P 和 P ′ P P′&#xff0c;对应的匹配图像点(同名点)为 ( u , v ) (u, v) (u,v) 和 ( u ′ , v ′ ) (u, v) (u′,v′)&#xff0c;目标是求解三维点 X [ X x , X y , X z , 1 ] T X [X_x, X_y, X_z, 1]^T X…...

【Linux三剑客】awk命令使用

AWK 编程语言中的变量 AWK 提供了许多可在模式和操作中使用的内置变量。最常用的变量是 - NR - 表示当前记录&#xff08;行&#xff09;号 NF - 表示输入记录中的字段总数。 $0 - 整个当前记录。 $1, $2, $3, … - 当前记录中的第一个、第二个、第三个…字段。 查找passwd中…...

Python CATIA二次开发实战:CATIA产品号批量同步文件名工具开发

引言 在汽车/航空制造领域&#xff0c;CATIA文件的结构化管理直接影响着PLM系统数据一致性。笔者近期开发的增强型产品号同步工具&#xff0c;成功解决了工程实践中文件名与产品名称不同步的痛点问题。本文将从技术实现、功能亮点、应用场景三个维度进行深度解析。 一、技术方…...

我的两个医学数据分析技术思路

我的两个医学数据分析技术思路 从临床上获得的或者公共数据库数据这种属于观察性研究&#xff0c;是对临床诊疗过程中自然产生的数据进行分析而获得疾病发生发展的规律等研究成果。再细分&#xff0c;可以分为独立危险因素鉴定和预测模型构建两种。 独立危险因素鉴定是一直以…...

操作系统之进程状态、优先级和切换与调度

文章目录 1. 进程状态1.1 课本名词提炼1.2 运行&阻塞&挂起1.2.1 运行1.2.2 阻塞1.2.3 挂起 1.3 理解内核链表1.4 Linux中的内核解释1.5 进程状态的查看1.6 Z(zombie)——僵尸进程1.6.1 创建僵尸进程1.6.2 僵尸进程的危害 1.7 孤儿进程 2. 进程优先级2.1 基本概念2.2 查…...

[免费]微信小程序(图书馆)自习室座位预约管理系统(SpringBoot后端+Vue管理端)(高级版)【论文+源码+SQL脚本】

大家好&#xff0c;我是java1234_小锋老师&#xff0c;看到一个不错的微信小程序(图书馆)自习室座位预约管理系统(SpringBoot后端Vue管理端)(高级版)&#xff0c;分享下哈。 项目视频演示 【免费】微信小程序(图书馆)自习室座位预约管理系统(SpringBoot后端Vue管理端)(高级版…...

你使用过哪些 Java 并发工具类?

你的回答&#xff08;口语化&#xff0c;面试场景&#xff09; 面试官&#xff1a;你使用过哪些 Java 并发工具类&#xff1f; 你&#xff1a; 好的&#xff0c;我结合项目经验来说说常用的并发工具类&#xff1a; CountDownLatch 作用&#xff1a;等所有线程就绪后再触发任务…...

模板方法模式的C++实现示例

核心思想 模板方法设计模式是一种行为设计模式&#xff0c;它定义了一个算法的框架&#xff0c;并将某些步骤的具体实现延迟到子类中。通过这种方式&#xff0c;模板方法模式允许子类在不改变算法结构的情况下重新定义算法的某些步骤。 模板方法模式的核心在于&#xff1a; ​…...

国产编辑器EverEdit - 脚本(解锁文本编辑的无限可能)

1 脚本 1.1 应用场景 脚本是一种功能扩展代码&#xff0c;用于提供一些编辑器通用功能提供不了的功能&#xff0c;帮助用户在特定工作场景下提高工作效率&#xff0c;几乎所有主流的编辑器、IDE都支持脚本。   EverEdit的脚本支持js(语法与javascript类似)、VBScript两种编程…...

越早越好!8 个反直觉的金钱真相|金钱心理学

很多人都追求财富自由&#xff0c;但成功的人少之又少。 这可能是因为&#xff0c;人们往往忽略了一些金钱的真相和常识。 01 金钱常识 & 真相 为了构建健康的金钱观&#xff0c;我读了一本有点反直觉&#xff0c;有点像鸡汤&#xff0c;但都是财富真相的书。 来自 Morg…...

linux docker相关指令

1、镜像操作 0&#xff09;、搜索&#xff1a;docker search 镜像名称 1&#xff09;、拉取&#xff1a;docker pull 2&#xff09;、推送&#xff1a;docker push 3&#xff09;、查看&#xff1a;docker images 4&#xff09;、查看所有镜像ID&#xff1a;d…...

实时采集到的语音进行语音识别

要在.NET Framework 4.8中使用C#实现离线实时语音识别&#xff0c;可以使用开源库Vosk&#xff08;支持离线ASR&#xff09;配合音频处理库NAudio。 步骤 1&#xff1a;安装依赖库 1.1. 安装NuGet包&#xff1a; - Install-Package NAudio&#xff08;处理音频输入&#xff09…...

Ollama 本地部署 DeepSeek R1 及 Python 运行 open-webui 界面(windows)

DeepSeek R1 ollama open-webui 本地部署&#xff08;windows&#xff09; DeepSeek-R1本地部署配置要求 Github地址&#xff1a;https://github.com/deepseek-ai/DeepSeek-R1?tabreadme-ov-file 模型规模最低 GPU 显存推荐 GPU 型号纯 CPU 内存需求适用场景1.5B4GBRTX 3…...

牛客周赛:84:C:JAVA

链接&#xff1a;登录—专业IT笔试面试备考平台_牛客网 来源&#xff1a;牛客网 题目描述 \hspace{15pt}本题为《D.小红的陡峭值&#xff08;三&#xff09;》的简单版本&#xff0c;两题的唯一区别在于本题的数据范围更小。 \hspace{15pt}小红定义一个字符串的陡峭值为&a…...

5. 前后端实现文件上传与解析

1. 说明 在实际开发中&#xff0c;比较常见的一个功能是需要在前端页面中选择系统中的某个文件上传到服务器中进行解析&#xff0c;解析后的文件内容可以用来在服务器中当作参数&#xff0c;或者传递给其它组件使用&#xff0c;或者需要存储到数据库中。所以本文就提供一种方式…...

SpringBoot 接入 豆包 火山方舟大模型

火山方舟控制台 开通模型推理、知识库 应用入口&#xff1b; 文档中心 各类接口说明及SDK 获取&#xff1b; 向量数据库VikingDB 文档 下翻找到有java操作案例&#xff1b; 实现目标功能效果&#xff1a; 通过SDK调用 豆包大模型&#xff0c;在代码内实现问答的效果&#xf…...

java 实现excel文件转pdf | 无水印 | 无限制

文章目录 目录 文章目录 前言 1.项目远程仓库配置 2.pom文件引入相关依赖 3.代码破解 二、Excel转PDF 1.代码实现 2.Aspose.License.xml 授权文件 总结 前言 java处理excel转pdf一直没找到什么好用的免费jar包工具,自己手写的难度,恐怕高级程序员花费一年的事件,也…...

【AI学习】三、AI算法中的向量

在人工智能&#xff08;AI&#xff09;算法中&#xff0c;向量&#xff08;Vector&#xff09;是一种将现实世界中的数据&#xff08;如图像、文本、音频等&#xff09;转化为计算机可处理的数值型特征表示的工具。它是连接人类认知&#xff08;如语义、视觉特征&#xff09;与…...

MySQL 8.0 OCP 英文题库解析(十三)

Oracle 为庆祝 MySQL 30 周年&#xff0c;截止到 2025.07.31 之前。所有人均可以免费考取原价245美元的MySQL OCP 认证。 从今天开始&#xff0c;将英文题库免费公布出来&#xff0c;并进行解析&#xff0c;帮助大家在一个月之内轻松通过OCP认证。 本期公布试题111~120 试题1…...

Linux --进程控制

本文从以下五个方面来初步认识进程控制&#xff1a; 目录 进程创建 进程终止 进程等待 进程替换 模拟实现一个微型shell 进程创建 在Linux系统中我们可以在一个进程使用系统调用fork()来创建子进程&#xff0c;创建出来的进程就是子进程&#xff0c;原来的进程为父进程。…...

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement

Cilium动手实验室: 精通之旅---13.Cilium LoadBalancer IPAM and L2 Service Announcement 1. LAB环境2. L2公告策略2.1 部署Death Star2.2 访问服务2.3 部署L2公告策略2.4 服务宣告 3. 可视化 ARP 流量3.1 部署新服务3.2 准备可视化3.3 再次请求 4. 自动IPAM4.1 IPAM Pool4.2 …...

Leetcode33( 搜索旋转排序数组)

题目表述 整数数组 nums 按升序排列&#xff0c;数组中的值 互不相同 。 在传递给函数之前&#xff0c;nums 在预先未知的某个下标 k&#xff08;0 < k < nums.length&#xff09;上进行了 旋转&#xff0c;使数组变为 [nums[k], nums[k1], …, nums[n-1], nums[0], nu…...

鸿蒙(HarmonyOS5)实现跳一跳小游戏

下面我将介绍如何使用鸿蒙的ArkUI框架&#xff0c;实现一个简单的跳一跳小游戏。 1. 项目结构 src/main/ets/ ├── MainAbility │ ├── pages │ │ ├── Index.ets // 主页面 │ │ └── GamePage.ets // 游戏页面 │ └── model │ …...

Neko虚拟浏览器远程协作方案:Docker+内网穿透技术部署实践

前言&#xff1a;本文将向开发者介绍一款创新性协作工具——Neko虚拟浏览器。在数字化协作场景中&#xff0c;跨地域的团队常需面对实时共享屏幕、协同编辑文档等需求。通过本指南&#xff0c;你将掌握在Ubuntu系统中使用容器化技术部署该工具的具体方案&#xff0c;并结合内网…...

高保真组件库:开关

一:制作关状态 拖入一个矩形作为关闭的底色:44 x 22,填充灰色CCCCCC,圆角23,边框宽度0,文本为”关“,右对齐,边距2,2,6,2,文本颜色白色FFFFFF。 拖拽一个椭圆,尺寸18 x 18,边框为0。3. 全选转为动态面板状态1命名为”关“。 二:制作开状态 复制关状态并命名为”开…...

Spring Boot SQL数据库功能详解

Spring Boot自动配置与数据源管理 数据源自动配置机制 当在Spring Boot项目中添加数据库驱动依赖&#xff08;如org.postgresql:postgresql&#xff09;后&#xff0c;应用启动时自动配置系统会尝试创建DataSource实现。开发者只需提供基础连接信息&#xff1a; 数据库URL格…...