序列化和反序列的学习

一:重谈协议

1 理解网络协议,可以把它想象成网络世界里的“交通规则”和“通用语言”。它是一套预先定义好的规则、标准和约定,使得不同设备、不同系统之间能够顺利地进行通信和数据交换。

我们从TCP协议上面理解一下,首先TCP服务是全双工的,怎么理解是全双工的呢,就是如果使用TCP服务,在客户端或者是服务端,都有两个缓冲区---一个是发送缓冲区,一个是接收缓冲区,我们在TCP接收发送文件不是用的是read,wirte吗,当要接收数据时,就从接收缓冲区里读数据交给用户空间,也就是从内核到用户,从而发送数据时,write函数就把用户空间的数据拷贝到发送缓冲区里,也就是从用户层到内核层。刷新什么的由TCP自己决定,全全交给了OS。所以TCP又叫做传输控制协议,里面有各种报头来确认数据传输的正确性。

这个确认数据的完整性或者正确性什么的,UDP和TCP就有了各自的特点,UDP是面向报文的,相当于快递,他就会要求传递报文必须是完整的,TCP是相当于自来水接水,所以呢他就有可能传过来的数据是一段一段的,我们在报头里就会有一些报文的长度啦,还有什么分隔符啦,分开报头和有效载荷了什么的。而这些要求就是我们今天要说的序列化和反序列化。

二:序列化和反序列化

我们所说的协议就是一种约定,客户端和服务端用的是同一套协议,举个例子,假设客户端发送请求,这个报文里面呢设置报文长度,有效载荷传过来的数据,性别啦,年龄了等等。而服务端接收到的数据就是已经序列化好的,就是把这些数据按照一定顺序排列好了给你发送过来了,接着就要把它反序列化,就是把这些存的东西一个一个的对应的从字符串里拿出来。说简单点了就是把字符串里的内容存放在对应的结构体里。协议是一种 "约定". socket api 的接口。

实现序列化和反序列化大体上有两种方法:

第一种就是自定义的方式,我自己规定传过来的数据 结构是什么样,从而让你读取到一个序列化好的,你可以通过反序列化拿到对应的数据,然后再返回一个序列化的结果,服务端接收到数据,也能通过反序列化拿到对应的结果

第二种方式就是用现成的 JSON 序列化 (Serialize),将内存中的数据结构(如对象、数组、字符串、数字、布尔值等)转换成符合 JSON 格式的字符串的过程。JSON 反序列(Deserialize),将一个符合 JSON 格式的字符串 解析并转换回内存中的数据结构(如对象、数组等)的过程。

三:使用序列化和反序列化实现一个简单的网络计算器

客户端和服务端没什么太大的变化。我们今天主要写的就是一个序列化和反序列化

我们直接用一个JSON的,比较方便,但是你前提的安装一下,

Ubuntu的:sudo yum install -y jsoncpp-devl

Centos的:sudo apt install -y jsoncpp-devl

1.我们需要构建两个类,一个请求,一个应答

请求在发送之前,客户端要进行序列化

请求在发送之后,服务端要进行反序列化

应答在发送之前,服务端要进行序列化

应答在发送之后,客户端要进行反序列化

我们实现的计算器比较简单 请求就是 一个数字 x 一个数字 再加一个符号,例子 30 + 20 

    int _x;int _y;char _oper;

然后把他进行序列化

     bool Serialize(std::string& out_string){Json::Value root;root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::StreamWriterBuilder wb;std::unique_ptr<Json::StreamWriter> w(wb.newStreamWriter());std::stringstream ss;w->write(root, &ss);out_string = ss.str();return true;}

这个out_string ,是作为输出型参数,把序列化的结果存进out_string

反序列化

 bool deserialize(std::string& in_string ){Json::Value root;Json::Reader reader;bool parsingSuccessful = reader.parse(in_string, root);if (!parsingSuccessful){return false;}_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}

in_string 就是作为一个输入型参数,把对应的结果存给对方就可以

应答的话就是一个结果,还有一个结果描述符,0设为正常,1设置为 / 或%  0了等等,这些都可以自己约定。

private:int _result;int _code;

序列化和反序列化道理一致,我们就不再分开展示了。

完整的代码Protocol.hpp

class Response
{public:Response() : _result(0), _code(0){}Response(int result, int code) : _result(result), _code(code){}~Response(){}bool Serialize(std::string& out_string){Json::Value root;root["result"] = _result;root["code"] = _code;Json::StreamWriterBuilder wg;std::unique_ptr<Json::StreamWriter>  w(wg.newStreamWriter());std::stringstream ss;w->write(root,&ss); //注意这里一个取地址,一个不取地址out_string = ss.str();return true;}bool deserialize(std::string& in_string){Json::Value root;Json::Reader reader;bool parsesucess = reader.parse(in_string,root);if(!parsesucess){std::cout<<"parseucess false"<<std::endl;return false;}_result = root["result"].asInt();_code = root["code"].asInt();return true;}int Result() const { return _result; }int Code() const { return _code; }void SetResult(int res) { _result = res;}void SetCode(int c) {_code = c;}private:int _result;int _code;
};

2.客户端

#include <iostream>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>
#include <string.h>
#include <unistd.h>
#include <cstdlib>
#include <string>
#include "Log.hpp"
#include "Common.hpp"
#include "Protocol.hpp"using namespace LogModule;int main(int argc , char* argv[])
{if(argc != 3 ){std::cout<<"Clinet need two arguments"<<std::endl;return 1;}std::string ip = argv[1];uint16_t port = std::stoi(argv[2]);int sockfd = socket(AF_INET,SOCK_STREAM,0);if(sockfd < 0){LOG(LogLevel::ERROR) << "create scokfd false";}struct sockaddr_in peer;memset(&peer,0,sizeof(peer));peer.sin_family = AF_INET;peer.sin_port = ::htons(port);peer.sin_addr.s_addr = ::inet_addr(ip.c_str());//客户端不用bind 绑定 ,tcp是面向连接的,connect 会自动绑定int n = ::connect(sockfd,CONV(&peer),sizeof(peer));if(n<0){LOG(LogLevel::ERROR)<<"connect false";return  1;}std::string message;while(true){int x, y;char oper;std::cout << "input x: ";std::cin >> x;std::cout << "input y: ";std::cin >> y;std::cout << "input oper: ";std::cin >> oper;Request req(x,y,oper);//序列化std::string message;req.Serialize(message);Encode(message);char inbuffer[1024];// n = ::write(sockfd, message.c_str(), message.size());n = ::send(sockfd,message.c_str(),message.size(),0);if(n > 0){//int m = ::read(sockfd, inbuffer, sizeof(inbuffer));int m =::recv(sockfd,inbuffer,sizeof(inbuffer),0);if(m > 0){inbuffer[m] = 0;std::string tmp = inbuffer;std::string content;Decode(tmp,&content);Response resp;resp.deserialize(content);std::cout<<resp.Result()<<resp.Code()<<std::endl;}elsebreak;}else break;}::close(sockfd);return 0;
}

3.服务端

TCPServer.hpp

#pragma once
#include <iostream>
#include "Common.hpp"
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <string.h>
#include <pthread.h>
#include <unistd.h>
#include <cstdlib>
#include <functional>
#include "Log.hpp"
#include "Internet.hpp"
#include "ThreadPool.hpp"#define BackWait 8using namespace LogModule;
using namespace ThreadPoolModule;
using task_t = std::function<void()>;
using handler_t = std::function<std::string(std::string& tmp)>;uint16_t defaultport = 8080;
std::string defaultip = "127.0.0.1";class TcpServer
{struct Thread {int sockfd;TcpServer* self;};
public:TcpServer(handler_t hander, uint16_t port = defaultport, std::string ip = defaultip): _port(port), _ip(ip), _isrunning(false), _listensockfd(-1),_hander(hander){}~TcpServer(){}void InitServer(){_listensockfd = ::socket(AF_INET,SOCK_STREAM,0);if(_listensockfd < 0){LOG(LogLevel::FATAL) << "create sockfd false";Die(1);}LOG(LogLevel::INFO) << "socket create success, sockfd is : " << _listensockfd;struct sockaddr_in local;bzero(&local, sizeof(local));local.sin_family = AF_INET;local.sin_port = ::htons(_port);local.sin_addr.s_addr = INADDR_ANY;// //面向连接的,还要等待随时被连接// //所以要设置为监听模式// 2. bindint n = ::bind(_listensockfd,CONV(&local),sizeof(local));if (n < 0){LOG(LogLevel::FATAL) << "bind error";Die(BIND_ERR);}LOG(LogLevel::INFO) << "bind success, sockfd is : " << _listensockfd;// 3. cs,tcp是面向连接的,就要求tcp随时随地等待被连接// tcp 需要将socket设置成为监听状态int m = ::listen(_listensockfd,8); if(m < 0 ){LOG(LogLevel::FATAL) <<"监听失败";Die(LISTEN_ERR);}LOG(LogLevel::FATAL) <<"监听成功";}void HandlerRequest(int sockfd){char buffer[1024];std::string package;while(true){//接受消息ssize_t n = ::read(sockfd,buffer,sizeof(buffer)-1);if(n>0){buffer[n] = 0;LOG(LogLevel::DEBUG)<<"接受到消息了#:"<<buffer;package += buffer;std::string cmd_result = _hander(package);::send(sockfd, cmd_result.c_str(), cmd_result.size(), 0); // 写入也是不完善}else if(n == 0){LOG(LogLevel::INFO) << "client quit: " << sockfd;break;}elsebreak;}}static void* ThreadHandler(void* args){//用线程也要等待回收(join) 必须等待回收的话就会阻塞,所以让线程自己结束释放资源pthread_detach(pthread_self());Thread* tmp = (Thread*)args;tmp->self->HandlerRequest(tmp->sockfd);return nullptr;}void Start(){_isrunning = true;while(true){struct sockaddr_in peer;socklen_t len = sizeof(peer);int sockfd = ::accept(_listensockfd,CONV(&peer),&len);//建立连接之后,这个对应的文件描述符才负责传信(接待)if (sockfd < 0){LOG(LogLevel::WARNING) << "accept error: " << strerror(errno);continue;}LOG(LogLevel::INFO) << "accept success, sockfd is : " << sockfd;InetAddr inetaddr(peer);LOG(LogLevel::INFO) << "client info: " << inetaddr.Addr();//version 0//HandlerRequest(sockfd);// version -1 多线程版本// pid_t pid = fork();//::signal(SIGCHLD,SIG_IGN);// if(pid == 0)// {//     //子进程再创建孙子进程,子进程直接退掉,由系统进程1 来回收管理孙子进程//     //子进程和父进程 各有一张文件描述符表 文件都是通过引用计数进行管理的//     //就像管道一样//     ::close(_listensockfd);//     if(fork() > 0)//     {//         exit(0);//     }//     HandlerRequest(sockfd);//     exit(0);// }// 给出建议父进程不要关闭文件描述符,这个设计叫权责分明// 现在语法执行没错,如果修改内容容易有错// ::close(sockfd);// pid_t waitid = ::waitpid(pid,nullptr,0);// if(waitid<0)// {//     LOG(LogLevel::ERROR)<<"回收父进程失败";// }//version 2 用多线程   // pthread_t pid;// Thread* data = new Thread;// data->self = this;// data->sockfd = sockfd;// pthread_create(&pid,nullptr,ThreadHandler,data);//version 3 线程池task_t f = std::bind(&TcpServer::HandlerRequest,this,sockfd);ThreadPool<task_t>::getInstance()->Equeue([&sockfd,this](){this->HandlerRequest(sockfd);});}}private:int _listensockfd; //这个文件描述符 只负责监听(也就是送客人,不负责招待)uint16_t _port;std::string _ip;bool _isrunning;handler_t _hander;
};

TCPServer.cc

#include"TcpServer.hpp"
#include<memory>
#include"Log.hpp"
#include"Calculator.hpp"
#include"Protocol.hpp"
#include<functional>
#include"Daemon.hpp"using namespace LogModule;using Cal_t = std::function<Response(const Request& req)>;// using cal_fun = std::function<Response(const Request &req)>;// // package一定会有完整的报文吗??不一定把
// // 不完整->继续读
// // 完整-> 提取 -> 反序列化 -> Request -> 计算模块,进行处理class Parse
{public:Parse(Cal_t cal):_cal(cal){}std::string Entry(std::string& package){std::string message;std::string resptr;while(Decode(package,&message)){LOG(LogLevel::DEBUG) << "Content: \n" << message;if(message.empty())break;//反序列化Request req;if(!req.deserialize(message))break;//计算std::string tmp;Response resp = _cal(req);resp.Serialize(tmp);//添加长度字段Encode(tmp);//拼接应答,这样的话有多少个需求都会处理,最后统一返回resptr+=tmp;}return resptr;}private:Cal_t _cal;
};int main()
{//ENABLE_FILE_LOG();//Daemon(false,false);Cal cal;Parse parse([&cal](const Request& req){return cal.entry(req);});std::shared_ptr<TcpServer> tserver = std::make_shared<TcpServer>([&parse](std::string &package){return parse.Entry(package);});tserver->InitServer();tserver->Start();return 0;
}

4.计算端

#include<iostream>
#include<string>
#include<memory>
#include"Protocol.hpp"class Cal
{public:Cal(){}Response entry(const Request& req){Response resp;switch (req.Oper()){case '+':resp.SetResult(req.X() + req.Y());break;case '-':resp.SetResult(req.X() - req.Y());break;case '*':resp.SetResult(req.X() * req.Y());break;case '/':{if (req.Y() == 0){resp.SetCode(1); // 1 就是除0}else{resp.SetResult(req.X() / req.Y());}}break;case '%':{if (req.Y() == 0){resp.SetCode(2); // 2 就是mod 0}else{resp.SetResult(req.X() % req.Y());}}break;default:resp.SetCode(3);break;}return resp;}private:
};

5.Log.hpp

#pragma once#include <iostream>
#include <cstdio>
#include <string>
#include <fstream>
#include <sstream>
#include <memory>
#include <filesystem> //C++17
#include <unistd.h>
#include <time.h>
#include "Mutex.hpp"
// + 日志的可变部分(<< "hello world"namespace LogModule
{using namespace Lock;std::string CurrentTime(){time_t time_stamp=::time(nullptr);struct tm curr;localtime_r(&time_stamp, &curr);char buffer[1024];snprintf(buffer,sizeof(buffer),"%4d-%02d-%02d %02d:%02d:%02d",curr.tm_year + 1900,curr.tm_mon + 1,curr.tm_mday,curr.tm_hour,curr.tm_min,curr.tm_sec);return buffer;}enum class LogLevel{DEBUG=1,INFO,WARNING,ERROR,FATAL};std::string Level2String(LogLevel level){switch (level){case LogLevel::DEBUG:return "DEBUG";case LogLevel::INFO:return "INFO";case LogLevel::WARNING:return "WARNING";case LogLevel::ERROR:return "ERROR";case LogLevel::FATAL:return "FATAL";default:return "None";}}class LogStrategy{public:virtual ~LogStrategy() = default;virtual void SyncLog(const std::string & message) = 0 ;};class ControlStrategy : public LogStrategy{public:ControlStrategy(){}~ControlStrategy(){}void SyncLog(const std::string& message){LockGuard lockguard(_mut);std::cout<<message<<std::endl;}private:Mutex _mut;};/*std::filesystem::exists(_logpath)*/const std::string defaultlogpath = "./log/";const std::string defaultlogname = "log.txt";class FileStrategy : public LogStrategy{public:FileStrategy(const std::string& logpath=defaultlogpath,const std::string& logname = defaultlogname):_logpath(logpath),_logname(logname){LockGuard lockguard(_mut);if(std::filesystem::exists(_logpath)){return;    }try{std::filesystem::create_directories(_logpath);}catch(std::filesystem::filesystem_error& t){std::cerr<<t.what()<<std::endl;}}~FileStrategy(){}void SyncLog(const std::string& message){LockGuard lockgurad(_mut);std::string log = _logpath+_logname;std::ofstream out(log,std::ios::app);if(!out.is_open()){std::cout<<"打开失败"<<std::endl;return;}out<<message<<"\n";out.close();}private:Mutex _mut;std::string _logpath;std::string _logname;};class Logger{public:Logger(){_strategy=std::make_shared<ControlStrategy>(); }void EnableControl(){_strategy=std::make_shared<ControlStrategy>(); }void EnableFile(){_strategy=std::make_shared<FileStrategy>();}~Logger(){}//一条完整的信息: [2024-08-04 12:27:03] [DEBUG] [202938] [main.cc] [16]class LogMessage{public:LogMessage(LogLevel level,const std::string &filename, int line,Logger& logger):_time(CurrentTime()),_level(level),_pid(::getpid()),_name(filename),_line(line),_logger(logger){std::stringstream ssbuffer;ssbuffer << "[" << _time << "] "<< "[" <<Level2String(_level) << "] "<< "[" << _pid << "] "<< "[" << _name << "] "<< "[" << _line << "] - ";_loginfo = ssbuffer.str();//std::cout<<_loginfo<<std::endl;         }template<class T>LogMessage &operator<<(const T& info){std::stringstream ss;ss<<info;_loginfo += ss.str();return *this;}~LogMessage(){if(_logger._strategy){_logger._strategy->SyncLog(_loginfo);}}private:std::string _time;LogLevel _level;;pid_t _pid;std::string _name;int _line;std::string _loginfo;Logger &_logger;};LogMessage operator()(LogLevel level, const std::string &filename, int line){return LogMessage(level, filename, line,*this);}private:std::shared_ptr<LogStrategy> _strategy;};Logger logger;
#define LOG(level) logger(level,__FILE__,__LINE__)
#define ENABLE_CONSOLE_LOG() logger.EnableControl()
#define ENABLE_FILE_LOG() logger.EnableFile()}

四 重谈七层协议

五层协议: 物理层  数据链路层 网络层 应用层  传输层 

七层协议:物理层  数据链路层  网络层 应用层  传输层  会话层 表示层

五层模型是将 OSI 七层模型中的会话层、表示层和应用层这三层合并成了一个单一的应用层。

为什么这样合并?
实际应用:在实际的 TCP/IP 协议栈中,会话管理(如建立、管理和终止会话)和数据表示(如数据加密、压缩、格式转换)的功能通常由应用程序本身或应用层协(如 HTTP、TLS/SSL)来实现,而不是由一个独立的、通用的协议层来处理。也就是说应用层实现大部分,所以为了不冲突,就由用户层自我决定。

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。
如若转载,请注明出处:http://www.pswp.cn/pingmian/94883.shtml
繁体地址,请注明出处:http://hk.pswp.cn/pingmian/94883.shtml

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

相关文章

计算机毕业设计 java 在线学习系统 基于 Java 的在线教育平台 Java 开发的学习管理系统

计算机毕业设计 java 在线学习系统fk01a40i &#xff08;配套有源码 程序 mysql数据库 论文&#xff09;本套源码可以先看具体功能演示视频领取&#xff0c;文末有联xi 可分享传统学习模式受时空限制&#xff0c;互动性不足&#xff0c;难以满足个性化学习需求。为打破限制&…

淘宝利用商品关键词获取商品信息指南

一、核心API接口选择接口名称功能描述适用场景taobao.items.search通过关键词搜索商品&#xff0c;支持分页、排序&#xff0c;返回商品列表&#xff08;含标题、价格、销量、图片等&#xff09;普通商品搜索、竞品监控、数据分析taobao.tbk.item.get淘宝客API&#xff0c;返回…

红黑树下探玄机:C++ setmultiset 的幕后之旅

目录 一、关联式容器 二、键值对 三、set 四、set的构造 五、set的iterator 六、set的Operations 七、multiset 一、关联式容器 序列式容器 &#xff1a; 在初阶阶段&#xff0c;我们已经接触过STL中的部分容器&#xff0c;比如&#xff1a;vector、list、deque、forwa…

Spring : 事务管理

1. 基本概念 事务&#xff08;Transaction&#xff09;是一组不可分割的操作单元&#xff0c;这些操作要么全部成功执行&#xff0c;要么全部失败回滚&#xff0c;不存在部分成功的情况。 事务具有ACID特性&#xff1a; 原子性&#xff08;Atomicity&#xff09;&#xff1a;事…

C# 一个投资跟踪程序的设计与实现:面向对象与设计模式的深度解析

在现代金融应用开发中&#xff0c;如何高效、灵活地构建投资跟踪系统&#xff0c;是每一个金融软件工程师必须面对的挑战。本文将围绕一个投资跟踪程序的设计与实现过程&#xff0c;深入剖析其背后的设计理念、架构模式以及具体实现细节。我们将通过面向对象编程、设计模式&…

存储的未来之战:RustFS如何用ZK框架重构分布式协调?

本篇文章目录 一、导火索&#xff1a;当数据洪峰撞上分布式协调的天花板 二、技术密码&#xff1a;ZK框架的三大重构 2.1 一致性哈希环的量子级进化 2.2 动态负载均衡的"神经反射" 2.3 跨云数据同步的"时空折叠" 三、未来战争&#xff1a;2026年存储…

模拟实现STL中的list容器

list前言一、list的节点结构设计二、迭代器设计三、list类的实现3.1 类的成员变量和类型定义3.2 构造函数与析构函数3.3 元素访问与迭代器接口3.4 插入与删除操作3.5 其他常用操作四、总结每文推荐前言 在C STL中&#xff0c;list是一个非常常用的容器&#xff0c;它基于双向循…

Debug-039-el-date-picker组件手动输入时间日期的问题处理

图1-外输入框图2-内输入框图3问题描述&#xff1a;这两天在迭代功能的时候&#xff0c;基本上碰到的问题都是出自这个“时间日期选择框”&#xff0c;昨天的bug38也是解决这个组件。如上图1和2所示&#xff0c;可以把图1中的输入框叫外输入框&#xff0c;图2中的输入框叫内输入…

docker-runc not installed on system

问题 Docker build时Dockerfile有RUN命令执行报错shim error: docker-runc not installed on system&#xff0c;如下&#xff1a;解决方法 修改/etc/docker/daemon.json&#xff0c;添加正面内容 {"runtimes": {"docker-runc": {"path": "…

【秋招笔试】2025.08.27华为秋招研发岗真题

📌 点击直达笔试专栏 👉《大厂笔试突围》 💻 春秋招笔试突围在线OJ 👉 笔试突围在线刷题 bishipass.com 题目一:智能温控系统监测 1️⃣:使用滑动窗口技术维护有效温度区间 2️⃣:利用单调队列高效维护窗口内的最大值和最小值 3️⃣:动态调整窗口边界,确保满足温…

Kafka 消费模型

文章目录1. 一个消费者组中只有 1 个消费者2. 一个消费者组中有 2 个消费者3. 消费者数量 > 分区数量4. 多个消费者读取同一个分区5. 消费者放入消费者组5.1 何时放入同一个消费者组5.2 何时放入不同的消费者组1. 一个消费者组中只有 1 个消费者 假设我们有一个 TopicT1&am…

【路由器】TP Link 路由器为何无法进入管理后台

TL-WR710N是TP Link在很多年前发布的一个迷你型的便携路由器&#xff0c;一插上还能用&#xff0c;直接reset打算重设密码&#xff0c;结果根据它给的192.168.1.253根本打不开。# 解决方法ping一下192.168.1.253&#xff0c;无法连接。这个问题本质上是 你电脑/手机的 IP 和路由…

LightGBM(Light Gradient Boosting Machine,轻量级梯度提升机)梳理总结

LGB微软团队在 2017 年提出的梯度提升树模型&#xff0c;核心定位是 “更高效的 XGBoost”—— 它在保持精度接近 XGBoost 的同时&#xff0c;通过“数据采样优化”“特征压缩”“树生长策略改进”三大创新&#xff0c;将训练速度提升 10-100 倍&#xff0c;内存消耗降低数倍&a…

毕业项目推荐:29-基于yolov8/yolov5/yolo11的光伏板检测识别系统(Python+卷积神经网络)

文章目录 项目介绍大全&#xff08;可点击查看&#xff0c;不定时更新中&#xff09;概要一、整体资源介绍技术要点功能展示&#xff1a;功能1 支持单张图片识别功能2 支持遍历文件夹识别功能3 支持识别视频文件功能4 支持摄像头识别功能5 支持结果文件导出&#xff08;xls格式…

【实时Linux实战系列】实时数据可视化技术实现

在当今数据驱动的世界中&#xff0c;实时数据可视化已成为理解和利用实时信息的关键工具。无论是在金融交易监控、工业生产监控、智能交通管理还是物联网设备监控中&#xff0c;能够将复杂的数据以直观的图表形式展示出来&#xff0c;对于快速决策和问题解决至关重要。实时数据…

【LeetCode每日一题】21. 合并两个有序链表 2. 两数相加

每日一题21. 合并两个有序链表题目总体思路算法步骤时间复杂度与空间复杂度代码2. 两数相加题目总体思路算法步骤时间复杂度与空间复杂度代码知识感悟2025.8.3021. 合并两个有序链表 题目 将两个升序链表合并为一个新的 升序 链表并返回。新链表是通过拼接给定的两个链表的所…

DVWA靶场通关笔记-文件包含(Impossible级别)

目录 一、源码分析 二、文件包含防范分析 1、明确指定允许包含的文件 2、拒绝所有未在白名单中的输入 3、总结 &#xff08;1&#xff09;白名单 (Allow List) &#xff08;2&#xff09;硬编码/映射 (Hardcoding/Mapping) &#xff08;3&#xff09;输入过滤 (Input F…

构建坚不可摧的数据堡垒:深入解析 Oracle 高可用与容灾技术体系

在当今数字化时代&#xff0c;数据是企业的核心资产&#xff0c;而承载这些数据的数据库系统的连续性与稳定性直接关系到企业的生死存亡。一次计划外的停机或灾难性的数据丢失&#xff0c;带来的不仅是经济上的巨大损失&#xff0c;更是对品牌信誉和客户信任的致命打击。因此&a…

【3D算法技术入门】如何基于建筑图片重建三维数字资产?

要基于建筑图片重建三维数字资产是一个复杂的计算机视觉任务&#xff0c;涉及图像采集、特征提取、相机姿态估计、稠密重建和三维模型优化等多个步骤。下面我将提供一个基于Python的解决方案框架&#xff0c;使用开源库实现从图片到三维模型的基本流程。 首先需要安装必要的库&…

⭐CVPR2025 自动驾驶半监督 LiDAR 分割新范式:HiLoTs 框架深度解析

&#x1f4c4;论文题目&#xff1a;HiLoTs: High-Low Temporal Sensitive Representation Learning for Semi-Supervised LiDAR Segmentation in Autonomous Driving ✍️作者及机构&#xff1a; R.D. Lin、Pengcheng Weng、Yinqiao Wang、Fei Wang&#xff08;西安交通大学软件…