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

自定义协议与序列反序列化

目录

引子: 

一、再谈 "协议"  

二、自定义协议与网络版计算器 

1.约定方案一: 

2.约定方案二:

3.我们采用的协议 

 三、网络计算器代码

 Log.hpp 日志

Makefile

Socket.hpp 套接字封装

 Protocol.hpp 协议 序列化反序列化 结构化数据格式规定

TcpServer.hpp 服务器主体代码封装

ServerCal.hpp 计算器逻辑

ServerCal.cc 服务器主函数

ClientCal.cc 客户端主函数 代码较少未作拆分

四、结果显示

1.telnet测试

 2.客户端测试

3.一次多个请求 客户端不显示结果

 4.显示服务器处理数据过程

五、自动序列反序列化方法 

json


引子: 

        我们在上一篇文章中以及提到,write,read这些接口本质上起到的是一个拷贝的作用。

        而以发送为例,发送缓冲区什么时候发,发多少,出错了该怎么办,这些都是由tcp决定的,这也是为什么tcp被称为传输控制协议。 

        tcp是操作系统网络模块的部分,是被写到源代码中的。我们把数据交给tcp就相当于把数据交给操作系统。整体上的逻辑和文件操作是非常类似的。

        但是对于接收方来讲,我们不能保证每次接收的数据都是 一个完整的报文,有可能一次有非常多的报文被读上来,也有可能有的报文只被读上来部分,因此这就需要我们在应用层定制协议以及序列反序列化了。

         我们程序员写的一个个解决我们实际问题, 满足我们日常需求的网络程序, 都是在应用层

一、再谈 "协议"  

        协议是一种 "约定". socket api的接口, 在读写数据时, 都是按 "字符串" 的方式来发送接收的. 如果我们要传输一些"结构化的数据" 怎么办呢? 

二、自定义协议与网络版计算器 

        例如, 我们需要实现一个服务器版的加法器. 我们需要客户端把要计算的两个加数发过去, 然后由服务器进行计算, 最后再把结果返回给客户端 

1.约定方案一: 

客户端发送一个形如"1+1"的字符串;
这个字符串中有两个操作数, 都是整形;
两个数字之间会有一个字符是运算符, 运算符只能是 + ;
数字和运算符之间没有空格; 

2.约定方案二:

 定义结构体来表示我们需要交互的信息;
发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体;
这个过程叫做 "序列化" 和 反序列化

         但是我们现在不推荐直接发送结构体对象,例如同一个结构体在不同的编译器编译,其大小有可能不一样(结构体内存对齐)

        不过Linux内核定义协议的时候用的就是互传结构体对象来做的,但是也做了非常多的后续工作来保证这种办法可行。

        我们网络版本的计算器大致思路如下,我们输入两个数以及符号,对方经过计算返回结果和标记位,标记位用于记录结果是否有效,如果无效则它会提供错误信息给我们 

        这里的两个结构体对象就是属于发送方和接收方的约定内容。

        我们再举一个例子,例如这里群聊信息的发送,双方约定好一个结构体对象,我们输入相应的内容进入结构体,然后将其转为 一个字符串进行发送,发送完成后再将这个字符串进行解析,还原为结构体,然后通过结构体对象来取得结果。

        其中,结构体中的多个字符串转化为一个字符串被称为序列化,而一个字符串还原成多个字符串被称为反序列化。序列和反序列化主要是方便网络进行收发。

        涉及到协议的定制,我们只需要知道定制怎么样的结构化数据就可以,然后再想办法把这个结构化数据整合成一个大字符串。但是接收方又怎么知道收到的字符串是否是完整的一个报文呢?

那么就需要我们自行加上一些将报文分隔的分隔符了。

3.我们采用的协议 

        同一个数字内是不可能出现空格的,因此我们把空格作为分隔数字与字符的标志。同时我们再以“\n”分隔不同的报文。这样其实就满足我们的需求了,但是这里我们还想添加一个长度的报头(这个长度是报文不包含\n的长度)。添加长度报头和添加“\n”都能满足我们的协议需求,但是为了后面调试方便,我们都采用。有了报文长度后,报文后的那个“\n”其实是可以不用添加的,但是添加之后我们调试时现象更明显,打印的时候会分行

        以及更进一步我们可以添加protocal select报头,我们可以根据不同的protocal select采取不同的协议,1是整数,2是浮点数等等,这里先不采用,只用长度报文。

        注意,本协议中采取“\n”作为分隔的标志,是因为我们计算器中的xopy形式是不可能存在“\n”的,因此可行。

 三、网络计算器代码

 Log.hpp 日志

#pragma once#include <iostream>
#include<time.h>
#include<stdarg.h>
#include <fcntl.h>
#include<unistd.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 3class Log
{
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 logmessage(int level,const char *format, ...)// {//     time_t t = time(nullptr);//时间戳//     struct tm *ctime = localtime(&t);//用时间戳得到一个结构体,可以从里面取年月日时分秒//     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);//把字符串存到leftbuffer里面//     va_list s;//     va_start(s, format);//     char rightbuffer[SIZE];//     vsnprintf(rightbuffer, sizeof(rightbuffer),format, s);//用这个库函数我们就不用自己作字符串解析了//     //格式 默认部分(左)+自定义部分(右)//     char logtxt[SIZE*2];//     snprintf(logtxt, sizeof(logtxt),"%s %s\n", leftbuffer, rightbuffer);//     printLog(level, logtxt);//暂时打印// }void printLog(int level, std::string logtxt){switch (printMethod){case Screen:std::cout << logtxt << std:: endl;break;case Onefile:printOneFile("LogFile" ,logtxt);break;case Classfile:printClassFile(level, logtxt);default: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);//LogFileif(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);//LogFile.Debug/Warning/FatalprintOneFile(filename, logtxt);}~Log()//这里析构只是为了让类看起来完整{}void operator()(int level,const char *format, ...){time_t t = time(nullptr);//时间戳struct tm *ctime = localtime(&t);//用时间戳得到一个结构体,可以从里面取年月日时分秒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);//把字符串存到leftbuffer里面va_list s;va_start(s, format);char rightbuffer[SIZE];vsnprintf(rightbuffer, sizeof(rightbuffer),format, s);//用这个库函数我们就不用自己作字符串解析了//格式 默认部分(左)+自定义部分(右)char logtxt[SIZE*2];snprintf(logtxt, sizeof(logtxt),"%s %s", leftbuffer, rightbuffer);printLog(level, logtxt);}
private:int printMethod;std :: string path;
};Log lg;

Makefile

.PHONY:all
all:servercal clientcalservercal:ServerCal.ccg++ -o $@ $^ -std=c++11
clientcal:ClientCal.ccg++ -o $@ $^ -std=c++11 -g.PHONY: clean 
clean:rm -f clientcal servercal 

Socket.hpp 套接字封装

#pragma#include <iostream>
#include <unistd.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <cstring>
#include "Log.hpp"enum
{SocketErr = 2,BindErr,ListenErr
};const int backlog = 10;class Sock
{
public:Sock(){}~Sock(){}public:void Socket(){sockfd_ = 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;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(sockfd_, (struct sockaddr *)&local, sizeof(local)) < 0){lg(Fatal, "socker error, %s: %d", strerror(errno), errno);exit(SocketErr);}}void Listen(){if (listen(sockfd_, backlog) < 0){lg(Fatal, "bind 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, "listen error, %s: %d", strerror(errno), errno);return -1;}char ipstr[64];inet_ntop(AF_INET, &peer.sin_addr, ipstr, sizeof(ipstr));*clientip = ipstr;*clientport = ntohs(peer.sin_port);return newfd;}int 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;}void Close(){close(sockfd_);}int Fd(){return sockfd_;}
private:int sockfd_;
};

 Protocol.hpp 协议 序列化反序列化 结构化数据格式规定

#pragma#include <iostream>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;
}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);//package = len_str + content_str + 2std::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(){}Request(int data1, int data2, char oper):x(data1),y(data2),op(oper){}public:bool Serialize(std::string *out){//struct => string "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;return true;}bool Deserialize(const std::string & in)//"x op y"{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);return true;}void DebugPrint(){std:: cout << "新请求构建完成 " << x << op << y << "= ?" << std::endl;}
public://x op yint x;int y;char op;
};class Response
{
public:Response(int res, int c):result(res), code(c){}Response(){}
public:bool Serialize(std::string *out){//"result code"//构建报文的有效载荷std::string s = std::to_string(result);s += blank_space_sep;s += std::to_string(code);*out = s;return true;}bool Deserialize(const std::string & in){std::size_t pos = in.find(blank_space_sep);if(pos == std::string::npos)return false;std::string part_left = in.substr(0,pos);std::string part_right = in.substr(pos+1);result = std::stoi(part_left);code = std::stoi(part_right);return true;}void DebugPrint(){std:: cout << "结果响应完成 " << "result: " << result << "code: " <<  code << std::endl;}
public:int result;int code;//0 可信,否则!0 具体是几表明对应的错误原因
};

TcpServer.hpp 服务器主体代码封装

#pragma
#include <functional>
#include <string>
#include "Log.hpp"
#include "Socket.hpp"
#include <signal.h>
#include "Log.hpp"using func_t = std::function<std::string(std::string &package)>;class TcpServer
{
public:TcpServer(uint16_t port, func_t callback):port_(port), callback_(callback){}bool InitServer(){listensock_.Socket();listensock_.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();std::string inbuffer_stream;//数据计算while(true){char buffer[128];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());//std::string info = callback_(inbuffer_stream);//if(info.empty())continue;//不处理,直到客户端把发来的信息填写正确//write(sockfd, info.c_str(),info.size());while(true)//这个while循环,在一次读只有一个报文以及不足一个报文的时候效果同上面的代码一样//而当一次读了好几个报文的时候 它也可以循环处理{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());// 我们在这里可以看到每次我们拿到一个response的时候 inbufferstream都会变短一些write(sockfd, info.c_str(),info.size());}}else if(n == 0)break;else break;}exit(0);}close(sockfd);}}~TcpServer(){}
private:uint16_t port_;Sock listensock_;func_t callback_;
};

ServerCal.hpp 计算器逻辑

#pragma <iostream>
#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);switch(req.op){case '+':resp.result = req.x + req.y;break;case '-':resp.result = req.x - req.y;case '*':resp.result = req.x * req.y;case '/':{if(req.y == 0)resp.code = Div_ZERO;else resp.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;}std::string Calculator(std::string &package){std::string content;bool r = Decode(package, &content);//"len"\n"10 + 20"\nif(!r)return "";Request req;r = req.Deserialize(content);//"10 + 20" ->x=10 op=+ y=20if(!r)return "";//解析不出来 或者序列化失败我们就return//并没有对报文进行修改//让它继续读,所以在TcpServer.hpp中是加等      即inbuffer_stream += buffer;content = "";Response resp = CalculatorHelper(req);//result=30 code=0resp.Serialize(&content);//"30 0"content = Encode(content);//"len"\n"30 0"\nreturn content;}
};

ServerCal.cc 服务器主函数

#include "TcpServer.hpp"
#include "ServerCal.hpp"static void Usage(const std::string &proc)
{std::cout << "\nUsage: " << proc << " port\n\n" << std::endl;
}
int main(int argc, char *argv[])
{if(argc != 2 ){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);ServerCal cal;TcpServer *tsvp = new TcpServer(port,std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));tsvp->InitServer();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(123, 456, '+');// 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;
}

ClientCal.cc 客户端主函数 代码较少未作拆分

#include "Socket.hpp"
#include <string>
#include <iostream>
#include <time.h>
#include "Protocol.hpp"
#include <unistd.h>
#include<assert.h>static void Usage(const std::string &proc)
{std::cout << "\nUsage:" << proc << " serverip serverport\n" << std::endl;
}
//./client ip port
int main(int argc, char *argv[])
{if(argc != 3){Usage(argv[0]);exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);Sock sockfd;sockfd.Socket();bool r = sockfd.Connect(serverip, serverport);if(!r)return 1;srand(time(nullptr) ^ getpid());int cnt = 1;const std::string opers = "+-*/%&^";std::string inbuffer_stream;while(cnt <= 10){std::cout <<"===================第" << cnt << "次测试..." << std::endl;int x = rand() % 100 + 1;usleep(123);int y = rand() % 100;usleep(321);char oper = opers[rand()%opers.size()];Request req(x, y, oper);req.DebugPrint();std::string package;req.Serialize(&package);package = Encode(package);std:: cout << "这是最新的发出请求: \n" << package;write(sockfd.Fd(), package.c_str(), package.size());std:: cout << "这是最新的发出请求: \n" << package;write(sockfd.Fd(), package.c_str(), package.size());std:: cout << "这是最新的发出请求: \n" << package;write(sockfd.Fd(), package.c_str(), package.size());std:: cout << "这是最新的发出请求: \n" << package;write(sockfd.Fd(), package.c_str(), package.size());//char buffer[1280];// ssize_t n = read(sockfd.Fd(), buffer, sizeof(buffer));//我们也无法保证我们能读到一个完整的报文// if(n > 0)// {//     buffer[n] = 0;//     inbuffer_stream += buffer;//     std::string content;//     bool r = Decode(inbuffer_stream, &content);//     assert(r);//服务器那边已经做过类似处理 这里就直接用assert判断了 注意,带assert的话需要在编译的时候加-g//     Response resp;//     r = resp.Deserialize(content);//     assert(r);//     resp.DebugPrint();// }sleep(1);std::cout <<"=====================================" << std::endl;cnt++; }sockfd.Close();return 0;
}

四、结果显示

1.telnet测试

 2.客户端测试

3.一次多个请求 客户端不显示结果

 4.显示服务器处理数据过程

可以看到拿走最后一个后,重新获取了报文 

五、自动序列反序列化方法 

我们现有json 和protobuf 方案来进行便捷的序列反序列化

json:可视化的序列反序列化,方便调试

protobuf:二进制流的序列反序列化方式,注重效率

json

里面大多数是key value的格式,一个json串实际上也就是一个字符串

要使用它我们需要安装第三方库 sudo yum install -y jsoncpp-devel

相关文章:

自定义协议与序列反序列化

目录 引子&#xff1a; 一、再谈 "协议" 二、自定义协议与网络版计算器 1.约定方案一: 2.约定方案二: 3.我们采用的协议 三、网络计算器代码 Log.hpp 日志 Makefile Socket.hpp 套接字封装 Protocol.hpp 协议 序列化反序列化 结构化数据格式规定 TcpSe…...

SAP-ABAP:ABAP异常处理与SAP现代技术融合—— 面向云原生、微服务与低代码场景的创新实践

专题三&#xff1a;ABAP异常处理与SAP现代技术融合 —— 面向云原生、微服务与低代码场景的创新实践 一、SAP技术演进与异常处理的挑战 随着SAP技术栈向云端、微服务化和低代码方向演进&#xff0c;异常处理面临新场景&#xff1a; Fiori UX敏感度&#xff1a;用户期望前端友…...

JavaScript面试题之消息队列

JavaScript消息队列详解&#xff1a;单线程的异步魔法核心 在JavaScript的单线程世界中&#xff0c;消息队列&#xff08;Message Queue&#xff09;是实现异步编程的核心机制&#xff0c;它像一位高效的调度员&#xff0c;让代码既能“一心多用”又避免卡顿。本文将深入剖析消…...

【低代码】如何使用明道云调用 Flask 视图函数并传参(POST 方法实践)

在自动化办公或业务流程管理中,明道云提供了强大的 HTTP 请求节点,可以直接调用第三方 API,包括我们常见的 Flask 服务端接口。本文将详细介绍如何使用明道云通过 POST 方法调用 Flask 视图函数并传参,包括配置要点与 Python 后端的参数接收方法。 一、场景介绍 我们希望…...

广州卓远VR受邀参加2025智能体育典型案例调研活动,并入驻国体华为运动健康联合实验室!

近日&#xff0c;“2025年智能体育典型案例调研活动”在东莞松山湖成功举办。本次调研活动由国家体育总局体育科学研究所和中国信息通信研究院联合主办&#xff0c;旨在深入贯彻中央关于培育新型消费的战略部署&#xff0c;通过激活智能健身产品消费潜力&#xff0c;加快运动健…...

【WebRTC】源码更改麦克风权限

WebRTC源码更改麦克风权限 仓库: https://webrtc.googlesource.com/src.git分支: guyl/m125节点: b09c2f83f85ec70614503d16e4c530484eb0ee4f...

spring cloud alibaba-Geteway详解

spring cloud alibaba-Gateway详解 Gateway介绍 在 Spring Cloud Alibaba 生态系统中&#xff0c;Gateway 是一个非常重要的组件&#xff0c;用于构建微服务架构中的网关服务。它基于 Spring Cloud Gateway 进行扩展和优化&#xff0c;提供了更强大的功能和更好的性能。 Gat…...

结课作业01. 用户空间 MPU6050 体感鼠标驱动程序

目录 一. qt界面实现 二. 虚拟设备模拟模拟鼠标实现体感鼠标 2.1 函数声明 2.2 虚拟鼠标实现 2.2.1 虚拟鼠标创建函数 2.2.2 鼠标移动函数 2.2.3 鼠标点击函数 2.3 mpu6050相关函数实现 2.3.1 i2c设备初始化 2.3.2 mpu6050寄存器写入 2.3.3 mpu6050寄存器读取 2.3.…...

Linux操作系统:信号

信号的基本介绍 信号是系统响应某个条件而产生的事件&#xff0c;进程接收到信号会执行响应的操作&#xff1b; &#xff08;1&#xff09;信号的储存位置 vim /usr/include/x86_64-linux-gnu/bits/signum.h 旧版 新版&#xff1a; vim /usr/include/x86_64-linux-gnu/bit…...

OpenCV CUDA模块特征检测与描述------用于创建一个最大值盒式滤波器(Max Box Filter)函数createBoxMaxFilter()

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 createBoxMaxFilter()函数创建的是一个 最大值滤波器&#xff08;Maximum Filter&#xff09;&#xff0c;它对图像中每个像素邻域内的像素值取最…...

有没有其他影视app可以像群晖video station一样可以被Windows的本地网络驱动器找到

你是在寻找可以通过Windows本地网络&#xff08;SMB共享&#xff09;访问的影视媒体服务程序&#xff0c;就像群晖的 Video Station 一样&#xff0c;可以浏览、播放或挂载电影资源。以下是一些可选方案&#xff1a; ✅ 具备与 Synology Video Station 类似功能&#xff0c;并支…...

【神经网络与深度学习】流模型的通俗易懂的原理

流模型&#xff08;Flow-based Model&#xff09;简介 引言 流模型是一种强大的生成模型&#xff0c;它通过可逆变换将简单的概率分布转化为复杂的数据分布。相比于扩散模型和生成对抗网络&#xff08;GAN&#xff09;&#xff0c;流模型可以精确计算数据的概率&#xff0c;并…...

OpenCV CUDA模块图像特征检测与描述------图像中快速检测特征点类cv::cuda::FastFeatureDetector

操作系统&#xff1a;ubuntu22.04 OpenCV版本&#xff1a;OpenCV4.9 IDE:Visual Studio Code 编程语言&#xff1a;C11 算法描述 cv::cuda::FastFeatureDetector 是 OpenCV 的 CUDA 加速模块中的一部分&#xff0c;用于在图像中快速检测特征点。FAST&#xff08;Features fro…...

分享一些多模态文档解析思路

多模态文档解析思路小记 作者&#xff1a;Arlene 原文&#xff1a;https://zhuanlan.zhihu.com/p/1905635679293122466 多模态文档解析内容涉及&#xff1a;文本、表格和图片 解析思路v1 基于mineru框架对pdf文件进行初解析 其具备较完整的布局识别和内容识别&#xff0c;并将…...

【OCCT+ImGUI系列】009-Geom2d-Geom2d_AxisPlacement

一、Geom2d_AxisPlacement 简介 在 OpenCASCADE 的二维几何库中&#xff0c;Geom2d_AxisPlacement 是一个用于定义二维坐标系的几何类&#xff0c;主要包含一个原点&#xff08;gp_Pnt2d&#xff09;和一个方向向量&#xff08;gp_Dir2d&#xff09;。它在构造与控制二维几何对…...

【深度学习:理论篇】--Pytorch之nn.Module详解

目录 1.torch.nn.Module--概述 2.torch.nn.Module--简介 3.torch.nn.Module--实现 3.1.Sequential来包装层 3.2.children和modules 1.torch.nn.Module--概述 1. PyTorch vs. Keras 的设计差异 Keras&#xff08;高层框架&#xff09;&#xff1a; 推荐通过继承 Layer 类…...

SQLMesh 宏操作符详解:@IF 的条件逻辑与高级应用

SQLMesh 的 IF 宏提供了一种在 SQL 查询中嵌入条件逻辑的方法&#xff0c;允许根据运行时条件动态调整查询结构。本文深入探讨 IF 的语法、使用场景及实际案例&#xff0c;帮助开发者构建更灵活、可维护的 SQL 工作流。 1. IF 宏简介 IF 是 SQLMesh 提供的条件逻辑宏&#xff…...

最新版Chrome浏览器调用ActiveX控件之eDrawings Viewer专用包v2.0.42版本发布

背景 eDrawings是一款轻量级的2D和3D浏览/可视化软件&#xff0c;主要用于查看和分享由SolidWorks创建的3D模型和2D工程图。它支持多种CAD文件格式&#xff0c;使得用户能够方便地在不同CAD系统之间转换和查看设计数据。‌适用于设计师和工程师之间的即时协作&#xff0c;通过电…...

人工智能应用时代:个人成长与职业突围的底层逻辑

当人工智能从实验室走向现实场景&#xff0c;从概念热词变为生产力工具&#xff0c;一场静默却深刻的变革正在重塑人类社会的运行规则。无论是算法驱动的智能推荐、语言模型支撑的自动化创作&#xff0c;还是工业机器人对传统生产线的颠覆&#xff0c;人工智能应用已渗透至社会…...

STM32之串口通信蓝牙(BLE)

一、串口通信的原理与应用 通信的方式 处理器与外部设备之间或者处理器与处理器之间通信的方式分两种&#xff1a;串行通信和并行通信。 串行通信 传输原理&#xff1a;数据按位依次顺序传输&#xff08;每一位占据固定的时间长度 MSB or LSB&#xff09; 优点&#xff1a…...

【Spring Boot】配置实战指南:Properties与YML的深度对比与最佳实践

目录 1.前言 2.正文 2.1配置文件的格式 2.2properties 2.2.1基础语法 2.2.2value读取配置文件 2.2.3缺点 2.3yml 2.3.1基础语法 2.3.2配置不同数据类型 2.3.3配置读取 2.3.4配置对象和集合 2.3.5优缺点 2.4综合练习&#xff1a;验证码案例 2.4.1分析需求 2.4.2…...

数据结构篇--优先级队列排序--实验报告

实验简介框架代码实验步骤运行结果实验总结 实验概述 优先队列排序算法的基本思想是&#xff1a; 将所有待排序元素依次插入到优先队列中&#xff0c;然后按照从大到小的顺序&#xff0c;通过重复删除优先队列中的最大元素&#xff0c;取出所有元素&#xff0c;从而实现排序…...

【图像大模型】基于深度对抗网络的图像超分辨率重建技术ESRGAN深度解析

基于深度对抗网络的图像超分辨率重建技术ESRGAN深度解析 一、技术背景与核心创新1.1 图像超分辨率技术演进1.2 核心技术创新对比 二、算法原理深度解析2.1 网络架构设计2.1.1 RRDB模块结构 2.2 损失函数设计2.2.1 对抗损失&#xff08;Adversarial Loss&#xff09;2.2.2 感知损…...

Ubuntu 20.04卸载并重装 PostgreSQL

在 Ubuntu 下彻底卸载并重新安装 PostgreSQL&#xff08;包括所有版本及其数据目录&#xff09;的步骤 下面是一个在 Ubuntu 下彻底卸载并重新安装 PostgreSQL&#xff08;包括所有版本及其数据目录&#xff09;的步骤。 文章目录 在 Ubuntu 下彻底卸载并重新安装 PostgreSQL&…...

debian系统redis-dump安装

1. ​Ruby 环境​ Redis-dump 是一个 Ruby 工具&#xff0c;需先安装 Ruby 和 RubyGems。 安装命令​&#xff1a; sudo apt update sudo apt install ruby-full build-essential[roota29d39f5fd10:/opt/redis-dump/bin# apt install ruby-full build-essential Reading pac…...

AI智能分析网关V4玩手机检测算法精准管控人员手机行为,搭建智慧化安防监管体系

一、背景​ 移动终端普及使随意用机成为常态&#xff0c;在生产车间、加油站、考场、手术室等场景&#xff0c;人员使用手机易引发生产事故、爆炸、作弊、仪器干扰等问题。传统人工巡查存在覆盖不足、响应慢、主观性强等局限&#xff0c;难以满足现代安全管理需求。AI智能分析…...

支持向量存储:PostgresSQL及pgvector扩展详细安装步骤!老工程接入RAG功能必备!

之前文章和大家分享过&#xff0c;将会出一篇专栏&#xff08;从电脑装ubuntu系统&#xff0c;到安装ubuntu的常用基础软件&#xff1a;jdk、python、node、nginx、maven、supervisor、minio、docker、git、mysql、redis、postgresql、mq、ollama等&#xff09;&#xff0c;目前…...

小土堆pytorch--神经网络-非线性激活线性层及其他层介绍

1. 神经网络-非线性激活 1.1 relu与sigmoid 1.1.1 ReLU&#xff08;Rectified Linear Unit&#xff0c;修正线性单元 &#xff09; 定义与数学表达&#xff1a;数学定义为 f ( x ) max ⁡ ( 0 , x ) f(x) \max(0, x) f(x)max(0,x) &#xff0c;即当输入 x > 0 x > …...

【Vue3】数据的返回和响应式处理(ref reactive)

目录 一、拉开序幕的setup 二、ref函数 2.1 访问对象的响应式处理 小结&#xff1a;ref函数 三、reactive函数 3.1 reactive同样也可以修改数组&#xff1a; 3.2 reactive小结&#xff1a; 四、Vue3中的响应式原理 4.1 vue2的响应式&#xff0c;对象属性的添加 4.2…...

【Rust智能指针】Rust智能指针原理剖析与应用指导

✨✨ 欢迎大家来到景天科技苑✨✨ &#x1f388;&#x1f388; 养成好习惯&#xff0c;先赞后看哦~&#x1f388;&#x1f388; &#x1f3c6; 作者简介&#xff1a;景天科技苑 &#x1f3c6;《头衔》&#xff1a;大厂架构师&#xff0c;华为云开发者社区专家博主&#xff0c;…...

C++ - 仿 RabbitMQ 实现消息队列(3)(详解使用muduo库)

C - 仿 RabbitMQ 实现消息队列&#xff08;3&#xff09;&#xff08;详解使用muduo库&#xff09; muduo库的基层原理核心概念总结&#xff1a;通俗例子&#xff1a;餐厅模型优势体现典型场景 muduo库中的主要类EventloopMuduo 的 EventLoop 核心解析1. 核心机制&#xff1a;事…...

Java异常处理全解析:从基础到自定义

目录 &#x1f680;前言&#x1f914;异常的定义与分类&#x1f4af;运行时异常&#x1f4af;编译时异常&#x1f4af;异常的基本处理 &#x1f31f;异常的作用&#x1f427;自定义异常&#x1f4af;自定义运行时异常&#x1f4af;自定义编译时异常 ✍️异常的处理方案&#x1…...

C++初阶-vector的模拟实现2

目录 1.vector已经实现的代码总结 2.vector::resize的模拟实现 3.vector::vector(const vector& v)拷贝构造函数的模拟实现 4.vector::operator(const vector& x)的模拟实现&#xff08;原始写法&#xff09; 5.vector::swap的模拟实现 6.vector::operator(const …...

【图数据库】--Neo4j 安装

目录 1.Neo4j --概述 2.JDK安装 3.Neo4j--下载 3.1.下载资源包 3.2.创建环境变量 3.3.运行 Neo4j 是目前最流行的图形数据库(Graph Database)&#xff0c;它以节点(Node)、关系(Relationship)和属性(Property)的形式存储数据&#xff0c;专门为处理高度连接的数据而设计。…...

elementui初学1

当然可以&#xff01;下面是从零开始创建一个最简单的 Element UI 程序的完整流程&#xff0c;基于 Vue 2 Element UI&#xff08;如果你想用 Vue 3&#xff0c;请告诉我&#xff0c;我可以给你 Element Plus 的版本&#xff09;。 ✅ 一、准备环境 确保你已经安装了&#xf…...

lanqiaoOJ 4185:费马小定理求逆元

【题目来源】 https://www.lanqiao.cn/problems/4185/learning/ 【题目描述】 给出 n&#xff0c;p&#xff0c;求 。其中&#xff0c; 指存在某个整数 0≤a<p&#xff0c;使得 na mod p1&#xff0c;此时称 a 为 n 的逆元&#xff0c;即 。数据保证 p 是质数且 n mod p≠0…...

计算机视觉与深度学习 | Python实现CEEMDAN-ISOS-VMD-GRU-ARIMA时间序列预测(完整源码和数据)

以下是结合CEEMDAN、ISOS-VMD、GRU和ARIMA的时间序列预测的Python完整实现方案。本方案包含完整的代码、数据生成逻辑和实现细节说明。 完整代码实现 import numpy as np import pandas as pd from PyEMD import CEEMDAN from vmdpy import VMD from scipy.optimize import di…...

前端开发遇到 Bug,怎么办?如何利用 AI 高效解决问题

前端开发遇到 Bug&#xff0c;怎么办&#xff1f;如何利用 AI 高效解决问题 作为前端开发者&#xff0c;遇到 Bug 几乎是日常。无论是样式错乱、功能异常&#xff0c;还是接口数据不对&#xff0c;Bug 总能让人头疼。但随着人工智能&#xff08;AI&#xff09;技术的发展&…...

博主总结框架

1.博主总结框架 1.1 计算机基础类&#xff08;数据结构、计算机网络、操作系统等&#xff09; &#xff08;1&#xff09;数据结构 &#xff08;2&#xff09;操作系统 &#xff08;3&#xff09;计算机网络 &#xff08;4&#xff09;其他 物联网入门框架 1.2 计算机图形…...

国产化Excel处理组件Spire.XLS for .NET系列教程:通过 C# 将 TXT 文本转换为 Excel 表格

在数据处理和管理场景中&#xff0c;将原始文本文件&#xff08;TXT&#xff09;高效转换为结构化的 Excel 电子表格是一项常见要求。对于那些需要自动生成报表或者处理日志文件的开发人员而言&#xff0c;借助 C# 实现 TXT 到 Excel 的转换工作&#xff0c;可以简化数据组织和…...

网络安全--PHP第一天

目标 熟悉信息传递架构 基于phpstydy-mysql-php 前置条件 需要先在数据库中创建相应的库和表名并配置表的结构 该文件为数据库配置文件 名字为config.php <?php $dbip localhost;//连接数据库的地址 远程连接需要输入ip等 $dbuser root;//连接数据库的用户 $dbpass ro…...

结构型:组合模式

目录 1、核心思想 2、实现方式 2.1 模式结构 2.2 实现案例 3、优缺点分析 4、适用场景 1、核心思想 目的&#xff1a;将总是在重复、迭代地显示的某种自相似性的结构&#xff08;部分与整体结构特征相似&#xff09;&#xff0c;例如树形结构&#xff0c;以统一的方式处…...

Node.js多版本安装工具NVM详细使用教程

一、nvm 简介 nvm&#xff08;Node Version Manager&#xff09;是一个用于管理多个 Node.js 版本的命令行工具&#xff0c;允许开发者在单个系统中轻松切换、安装和卸载不同版本的 Node.js。它是前端和后端开发中处理 Node.js 版本兼容性问题的核心工具之一。 二、nvm 安装 …...

深度解析 Java 中介者模式:重构复杂交互场景的优雅方案

一、中介者模式的核心思想与设计哲学 在软件开发的历史长河中&#xff0c;对象间的交互管理一直是架构设计的核心难题。当多个对象形成复杂的网状交互时&#xff0c;系统会陷入 "牵一发而动全身" 的困境。中介者模式&#xff08;Mediator Pattern&#xff09;作为行…...

(八)深度学习---计算机视觉基础

分类问题回归问题聚类问题各种复杂问题决策树√线性回归√K-means√神经网络√逻辑回归√岭回归密度聚类深度学习√集成学习√Lasso回归谱聚类条件随机场贝叶斯层次聚类隐马尔可夫模型支持向量机高斯混合聚类LDA主题模型 一.图像数字化表示及建模基础 二.卷积神经网络CNN基本原…...

深入剖析原型模式:原理、实现与应用实践

在软件开发的世界里,设计模式如同建筑师手中的蓝图,为复杂系统的构建提供了行之有效的解决方案。其中,原型模式(Prototype Pattern)作为创建型设计模式的重要一员,以其独特的对象创建方式,在提高代码复用性、增强系统灵活性等方面发挥着关键作用。本文将深入剖析原型模式…...

【论文阅读 | CVPR 2024 |RSDet:去除再选择:一种用于 RGB - 红外目标检测的由粗到精融合视角】

论文阅读 | CVPR 2024 |RSDet:去除再选择&#xff1a;一种用于 RGB - 红外目标检测的由粗到精融合视角 1.摘要&&引言2. 方法2.1 “由粗到细”融合策略2.2 冗余光谱去除模块&#xff08;RSR&#xff09;2.3 动态特征选择模块&#xff08;DFS&#xff09;2.4 去除与选择检…...

WinForms 应用中集成 OpenCvSharp 实现基础图像处理

引言 欢迎关注dotnet研习社&#xff0c;今天我们要讨论的主题是WinForms 应用中集成 OpenCvSharp 实现基础图像处理。 在常规的图像处理软件开发中&#xff0c;图像处理功能是这些应用程序的核心组成部分。无论是简单的照片编辑工具&#xff0c;还是复杂的计算机视觉应用&…...

apache http client连接池实现原理

在java开发中我们经常会涉及到http 请求接口&#xff0c;一般有几种方式&#xff1a; java自带的 HttpURLConnectionokHttpClientapache http client 一般我们使用apache http client会比较多点&#xff0c;在代码中会进行如下调用方式&#xff1a; private static class Htt…...

adb抓包

目录 抓包步骤 步骤 1: 获取应用的包名 步骤 2: 查看单个应用的日志 步骤 3: 使用日志级别过滤器 步骤 4: 高级日志过滤 可能的原因&#xff1a; 解决方案&#xff1a; 额外提示&#xff1a; 日志保存 抓包步骤 连接设备 adb devices 步骤 1: 获取应用的包名 首先…...