【Linux网络编程七】网络序列化和反序列化(网络版本计算器)
【Linux网络编程七】网络序列化和反序列化(网络版本计算器)
- 一.网络读取问题
- 【解决方案】
- 1.定制协议
- 2.序列化和反序列化
- 3.添加报头
- ①封包
- ②解包
- 4.框架总结
- 二.自定义协议:网络计算器协议
- Ⅰ.客户端发送请求,服务器端接收请求
- 1.构建请求(结构化数据)
- 2.请求序列化
- 3.添加报头,发送到网络
- 4.服务器读取请求
- 5.解除报头
- 6.请求反序列化
- Ⅱ.服务器端发送响应,客户端接收响应
- 1.构建响应(结构化数据)
- 2.响应序列化
- 3.添加报头,发送到网络
- 4.客户端读取响应
- 5.解除报头
- 6.响应反序列化
- 三.自动序列化和反序列化Json
- 四.理解OSI七层协议
一.网络读取问题
在网络通信时,通信双方是无法保证读取时,能够正确的读取到想要的内容的。
这是什么意思呢?
【解决方案】
1.定制协议
什么叫定制协议呢?就是让双方都要能知道,约定好的一些字段,然后以结构化的形式发送和接收。
定制的协议双方都要能认识才可以通信。不然一方认识,另一方不认识就无法通信了。
2.序列化和反序列化
定制好结构体后,然后构建一个对象,是不是就可以直接发送过去了呢?
当然不可以!为什么呢?
1.我们不能直接将结构体对象直接发送给对端机器,因为双方可能机器不同,对于结构体的解读会不一样,最终就会解析错误。所以通常我们不直接发送结构化数据到网络里,而是发送字符串形式的数据给对端。
2.也就是我们需要在构建完结构化数据后,在发送到网络里之前还需要将它转换成字符串形式,才能发送。这个过程就叫做序列化
3.而对端机器从网络里接收到字符串后,它并不认识这个字符串是什么意思,只有将这个字符串转成结构化化数据,它才能知道对方发送的是什么信息。而将字符串数据再转成结构化数据,就叫做反序列化。
3.添加报头
那么问题又回来了,对端是如何正确获取到想要的完整报文的呢?
我们可以利用一些特殊字符,来区别报文与报文。而如果想要对方准确的接收到一个完整的报文,那么就可以通过在报文前面添加一个长度单位,标识这个报文总长度有多少,一旦对端读取到这个长度,就可以直接从后面截取报文的长度,就可以直接完整的获取到一个报文。
而在报文前面添加一个长度单位这个行为,我们可以称为添加一个报头。
添加报头的目的就是为了让对端在读取的时候,能够根据报头,来完整的获取一个报文。所以添加报头也是属于定制协议的部分。通信的双方都要能识别。
①封包
所以构建完结构化数据后,在发送到网络之前,需要将之转换成字符串形式,也就是序列化。然后为了让对端能够准确的获取到一个完整的报文,我们还需要对这个字符串添加报头。
②解包
对端获取到从网络里发送来的数据后,并不理解是什么意思,所以需要反序列化,将字符串数据转换成结构化数据,这样它就可以理解是什么意思了。
但是这里存在的问题就是:它并不能确定对方发送的是一个报文还是多个报文,还是半个报文。因为从网络里发送来的就是一个字符串形式的数据。它分析不出来。而反序列化,是以一个完整的报文进行反序列化的,因为当时序列化的时候就是一个完整的结构体数据进行序列化的。
所以定制协议的人,考虑到这点,就在报文的前面添加了一个报头:表明报文的长度的字段。这样只要对端接收到报文,然后依据报头,就能分析出对方发送的数据是否是完整的了。如果是完整的,那么直接获取有效的报文。如果不是完整的,那么重新去读取,如果是多个报文,那么我们只要一个完整的报文即可。剩下的等下次再处理。
4.框架总结
网络部分套接字:
Socket.hpp
#pragma once
//将网络套接字编程部分直接封装打包,因为服务器和客户端都需要使用创建套接字等操作。
#include <iostream>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include "Log.hpp"
#include <cstdlib>
#include <cstring>
#include <unistd.h>
enum
{SocketErr=1,BindErr,ListenErr,
};
const int backlog=10;
class Sock
{public:Sock(){}~Sock(){}void Socket()//创建套接字{_socket=socket(AF_INET,SOCK_STREAM,0);if(_socket<0){lg(Fatal,"socket err:%s :%d",strerror(errno),errno);exit(SocketErr);}}void Bind(uint16_t &port)//绑定套接字{struct sockaddr_in local;memset(&local,0,sizeof(local));local.sin_addr.s_addr=INADDR_ANY;//将ip地址初始化成0local.sin_family=AF_INET;local.sin_port=htons(port);if(bind(_socket,(struct sockaddr*)&local,sizeof(local))<0){lg(Fatal,"bind err :%s :%d",strerror(errno),errno);exit(BindErr);}}void Listen()//将套接字设置成监听状态{if(listen(_socket,backlog)<0){lg(Fatal,"listen err :%s :%d",strerror(errno),errno);exit(ListenErr);}}int Accept(std::string *clientip,std::uint16_t* clientport)//服务器获取连接,并获取对方的网络信息,将获取的到的新连接交给服务函数操作{struct sockaddr_in client;socklen_t len=sizeof(client);int newsock=accept(_socket,(struct sockaddr*)&client,&len);if(newsock<0){lg(Warning,"accept err :%s :%d",strerror(errno),errno);return -1;}*clientport=ntohs(client.sin_port);char Clientip[32];inet_ntop(AF_INET,&client.sin_addr,Clientip,sizeof(Clientip));*clientip=Clientip;return newsock;}bool Connect(const std::string &serverip,const uint16_t &serverport)//需要知道要连接的对方的网络信息{struct sockaddr_in local;socklen_t len=sizeof(local);memset(&local,0,sizeof(local));local.sin_family=AF_INET;local.sin_port=htons(serverport);inet_pton(AF_INET,serverip.c_str(),&local.sin_addr);int n=connect(_socket,(struct sockaddr*)&local,len);if(n==-1){std::cerr<<"connect to"<<serverip<<":"<<serverport<<"error"<<std::endl;return false;}return true;}void Close(){close(_socket);}int Fd(){return _socket;}
private: int _socket;
};
协议部分
Protocol.hpp
#pragma once
//#define MySelf 1
// 在网络通信之前,我们服务器端和客户端都需要知道协议。我们也可以自己定制协议,这个协议要被双方都能识别
// 比如我们可以定制一个计数器协议。协议就是一种约定,除了数据本身还有其他的字段。
// 1.我们要求将数据以结构化的形式保存这样双方都可以识别这个结构体对象,但传入网络里时,需要转换成字符类型。这个过程就是序列化.序列化的过程就是在构建有效载荷
// 2.对方接收到字符串类型的数据时,想要用服务操作时,发现是不能操作的,是因为它不认识,这时还需要将字符类型转成结构体类型,这个过程叫做反序列化。
// 3.为了能让对方接收时,能接收读取到对方想要的完整报文时,我们采取添加报头的形式来解决。
// 4.所以在将报文传入到网络里时,还需要添加报文,当对端接收到报文时,想要对它进行处理之前,还需要将报文的报头解包才可以正确处理。
#include <iostream>
#include <jsoncpp/json/json.h>
#include <string>
const std::string blank_space = " ";
const std::string protocol_space="\n";
// 封包:报文在发送到网络之前需要添加一些报头,来达到一些要求
std::string Encode(const std::string &content)//content就是有效载荷
{//"x + y"------>"len"\n"x + y"\n" 添加了一个报文长度和两个\nstd::string packpage=std::to_string(content.size());packpage+=protocol_space;packpage+=content;packpage+=protocol_space;return packpage;
}// 解包:对端读取到报文(可能读取到的不是想要的,根据原先添加上去的报头来获取准确想要的报文),想要处理它,需要先解除报头才能处理
bool Decode(std::string &packpage, std::string *content)
{ //"len"\n"x + y"\n"---->"x + y"std::size_t pos=packpage.find(protocol_space);if(pos==std::string::npos)return false;std::string len_str=packpage.substr(0,pos);//判断一下是否读取的内容是全部的std::size_t len =std::stoi(len_str);std::size_t total_len=len_str.size()+len+2;if(packpage.size()<total_len)//说明不是一个完整的报文return false;*content=packpage.substr(pos+1,len);//为了真正的拿走报文,还需要将响应inbuffer里的报文移除erase,这样才是真正的拿走报文packpage.erase(0,total_len);return true;
}class Request
{
public:Request(){}Request(int data1, int data2, char op) : _x(data1), _y(data2), _op(op) // 最初形成结构化数据{}bool Serialize(std::string *out) // 序列化,单纯的就是将结构体转换成字符串{
#ifdef MySelf // 构建报文的有效载荷// struct==》"x + y"std::string s = std::to_string(_x);s += blank_space;s += _op;s += blank_space;s += std::to_string(_y);*out = s;return true;#elseJson::Value root;//定义一个万能对象,可以存储数据,k-v形式的结构体root["x"]=_x;root["y"]=_y;root["op"]=_op;//Json::FastWriter w;Json::StyledWriter w;*out=w.write(root);//序列化成字符串return true; #endif}bool Deserialize(std::string &in) // 反序列化,就单纯的将字符串类型转成结构体{
#ifdef MySelf //"x + y"==>struct//获取左操作数xstd::size_t left=in.find(blank_space);if(left==std::string::npos)return false;std::string part_x=in.substr(0,left);//获取右操作数ystd::size_t right=in.rfind(blank_space);if(right==std::string::npos)return false;std::string part_y=in.substr(right+1);//获取操作码opif(left+2!=right)return false;_op=in[left+1];_x=std::stoi(part_x);_y=std::stoi(part_y);return true;
#elseJson::Value root;//定义一个万能对象,将序列化的数据存储在里面Json::Reader r;r.parse(in,root);//将数据存到万能对象里后,我们就可以根据key值找到_x=root["x"].asInt();_y=root["y"].asInt();_op=root["op"].asInt();return true;
#endif }void DebugPrint(){std::cout<<"新请求构建完毕:"<<_x<<_op<<_y<<"=???"<<std::endl;}
public: // x + yint _x;int _y;char _op;
};
class Response
{
public:Response(int reslut, int code) : _reslut(reslut), _code(code){}Response(){}bool Serialize(std::string *out) // 序列化,单纯的就是将结构体转换成字符串{
#ifdef MySelf//"reslut code"//构建报文的有效载荷std::string s=std::to_string(_reslut);s+=blank_space;s+=std::to_string(_code);*out=s;return true;
#elseJson::Value root;root["reslut"]=_reslut;root["code"]=_code;//Json::FastWriter w;Json::StyledWriter w;*out=w.write(root);return true;
#endif }bool Deserialize(std::string &in){
#ifdef MySelf//"reslut code"-->结构体类型std::size_t pos=in.find(blank_space);if(pos==std::string::npos)return false;std::string part_left=in.substr(0,pos);std::string part_right=in.substr(pos+1);_reslut=std::stoi(part_left);_code=std::stoi(part_right);return true;
#elseJson::Value root;Json::Reader r;r.parse(in,root);//将字符串数据存到万能对象里_reslut=root["reslut"].asInt();_code=root["code"].asInt();return true;
#endif}void DebugPrint(){std::cout<<"结果响应完成,reslut: "<<_reslut<<",code: "<<_code<<std::endl;}public:int _reslut;int _code;
};
服务器服务部分:
ServerCal.hpp
#pragma once
#include "Protocol.hpp"
#include <iostream>
#include <string>
// 服务器端,从网络里读取到数据后,就要进行处理服务。
// 1.首先需要对报文进行解包,2.解包后还需要将报文转成结构体类型对方才能识别
enum
{Div_Zero = 1,Mod_Zero,Other_Oper
} ;
class ServerCal
{
public:Response Calculatorhelpor(const Request &req){Response resp(0, 0);switch (req._op){case '+':resp._reslut = req._x + req._y;break;case '-':resp._reslut = req._x - req._y;break;case '*':resp._reslut = req._x * req._y;break;case '/':{if (req._y == 0)resp._code = Div_Zero;elseresp._reslut = req._x / req._y;}break;case '%':{if (req._y == 0)resp._code = Mod_Zero;elseresp._reslut = req._x % req._y;}break;default:resp._code=Other_Oper;break;}return resp;}std::string Calculator(std::string &package){std::string content; //"len""\n""20 + 10""\n"bool r = Decode(package, &content); //"20 + 10"if (!r)return "";Request req; // 反序列化r = req.Deserialize(content);if (!r)return "";// 服务器端解包获取到报文后,就可以进行计算,再将计算结果返回回到网络里,网络里需要序列化的数据Response res = Calculatorhelpor(req); // reslut=30 code=0content = "";res.Serialize(&content); //"30 0"content = Encode(content); //"len""\n""30 0""\n"return content;}
};
二.自定义协议:网络计算器协议
Ⅰ.客户端发送请求,服务器端接收请求
ClientCal.cc
#include <iostream>
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include "Socket.hpp"
#include "Protocol.hpp"
void Usage(std::string proc)
{std::cout<<"\n\rUsage: "<<proc<<" port[1024+]\n"<<std::endl;
}
//./tcpclient ip port
int main(int args,char* argv[])
{if(args!=3){Usage(argv[0]);exit(1);}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;std::string oper="+-*/%=$";std::string inbuffer_stream;while(cnt<=10){std::cout<<"========第"<<cnt<<"次测试"<<"============"<<std::endl;//1.开始构建请求int x=rand()%100+1;usleep(1234);int y=rand()%100;usleep(4321);char op=oper[rand()%oper.size()];Request req(x,y,op);//2.请求构建完毕req.DebugPrint();//3.数据序列化形成报文std::string content;req.Serialize(&content);//4.添加报头std::string packpage=Encode(content);//5.发送到网络里write(sockfd.Fd(),packpage.c_str(),packpage.size());//6.接收服务器端发送来的响应char buffer[128];ssize_t n=read(sockfd.Fd(),buffer,sizeof(buffer));//6.1处理读取if(n>0){buffer[n]=0;inbuffer_stream+=buffer;//接收到的是一个协议报文"len"\n"reslut code"\nstd::cout<<std::endl;std::cout<<"获取到的网络答应:"<<std::endl;std::cout<<inbuffer_stream<<std::endl;//将从网络里获取到的报文打印出来//7.首先需要解包检测std::string content;bool r =Decode(inbuffer_stream,&content);assert(r);//8.反序列化,将答应变成客户端可认识的形式Response resp;r=resp.Deserialize(content);assert(r);//9.结果响应完成resp.DebugPrint();}std::cout<<"============================="<<std::endl;sleep(1);cnt++;}sockfd.Close();
}
1.构建请求(结构化数据)
2.请求序列化
3.添加报头,发送到网络
4.服务器读取请求
5.解除报头
6.请求反序列化
Ⅱ.服务器端发送响应,客户端接收响应
Main.cc
#include "Tcpserver.hpp"
#include "ServerCal.hpp"
#include <memory>
void Usage(std::string proc)
{std::cout << "\n\rUsage: " << proc << " port[1024+]\n"<< std::endl;
}int main(int args, char *argv[])
{if (args != 2){Usage(argv[0]);exit(0);}uint16_t port = std::stoi(argv[1]);ServerCal cal;Tcpserver *tcpsvr = new Tcpserver(port, std::bind(&ServerCal::Calculator, &cal, std::placeholders::_1));tcpsvr->Init();tcpsvr->Start();return 0;
}
Tcpserver.hpp
#pragma once
#include <iostream>
#include <string>
#include <functional>
#include "Log.hpp"
#include <signal.h>
#include "Socket.hpp"
#include "ServerCal.hpp"
Sock sock;
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 Init(){_listensock.Socket(); // 创建套接字_listensock.Bind(_port); // 绑定套接字_listensock.Listen(); // 将套接字设置成监听状态lg(Info, "init server...done");return true;}void Start() // 启动服务器{ // 启动之前需要先忽略一些信号signal(SIGPIPE, SIG_IGN);signal(SIGCHLD, 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){// 1.读取网络中的数据流char buffer[128];size_t n = read(sockfd, buffer, sizeof(buffer));if (n > 0){buffer[n]=0;//将读取的报文进行处理inbuffer_stream+=buffer;//注意读取的内容必须是一个完整的内容,不然调用回调时,就回调用失败lg(Debug,"获取的网络请求:\n %s",inbuffer_stream.c_str());std::string info=_callback(inbuffer_stream);if(info.empty())continue;//如果进行计算时,发现报文有问题,就重新回来读取。// 2.将处理的结果发送回网络中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;
};
1.构建响应(结构化数据)
2.响应序列化
3.添加报头,发送到网络
4.客户端读取响应
5.解除报头
6.响应反序列化
三.自动序列化和反序列化Json
bool Serialize(std::string *out) // 序列化,单纯的就是将结构体转换成字符串{Json::Value root;//定义一个万能对象,可以存储数据,k-v形式的结构体root["x"]=_x;root["y"]=_y;root["op"]=_op;//Json::FastWriter w;Json::StyledWriter w;*out=w.write(root);//序列化成字符串return true; }bool Deserialize(std::string &in) // 反序列化,就单纯的将字符串类型转成结构体{Json::Value root;//定义一个万能对象,将序列化的数据存储在里面Json::Reader r;r.parse(in,root);//将数据存到万能对象里后,我们就可以根据key值找到_x=root["x"].asInt();_y=root["y"].asInt();_op=root["op"].asInt();return true;}
四.理解OSI七层协议
相关文章:

【Linux网络编程七】网络序列化和反序列化(网络版本计算器)
【Linux网络编程七】网络序列化和反序列化(网络版本计算器) 一.网络读取问题【解决方案】1.定制协议2.序列化和反序列化3.添加报头①封包②解包 4.框架总结 二.自定义协议:网络计算器协议Ⅰ.客户端发送请求,服务器端接收请求1.构建请求(结构化…...

算法打卡day17|二叉树篇06|Leetcode 654.最大二叉树、617.合并二叉树、700.二叉搜索树中的搜索、98.验证二叉搜索树
算法题 Leetcode 654.最大二叉树 题目链接:654.最大二叉树 大佬视频讲解:最大二叉树视频讲解 个人思路 大概思路就是在数组中 找最大值的节点作为当前节点,用最大值的index切割左右子树的区间,往复循环到数组元素为0; 解法 递…...

C语言之数据在计算机内部的存储
文章目录 一、前言二、类型的基本归类1、整型家族2、浮点数家族3、构造类型4、指针类型 三、整型在内存中的存储1、原码、反码、补码1.1 概念1.2 原码与补码的转换形式1.3 计算机内部的存储编码 2、大小端介绍~~2.1 为什么要有大端和小端之分?2.2 大(小&…...

程序人生——Java中基本类型使用建议
目录 引出Java中基本类型使用建议建议21:用偶判断,不用奇判断建议22:用整数类型处理货币建议23:不要让类型默默转换建议24:边界、边界、还是边界建议25:不要让四舍五入亏了一方 建议26:提防包装…...

Pikachu 靶场搭建
文章目录 环境说明1 Pikachu 简介2 Pikachu 安装 环境说明 操作系统:Windows 10PHPStudy 版本: 8.1.1.3Apache 版本:2.4.39MySQL 版本 5.7.26 1 Pikachu 简介 Pikachu是一个使用“PHP MySQL” 开发、包含常见的Web安全漏洞、适合Web渗透测试学习人员练…...

机器学习-绪论
机器学习致力于研究如何通过计算的手段、利用经验来改善系统自身的性能。在计算机系统中,“经验”通常以“数据”的形式存在,因此,机器学习所研究的主要内容,是关于在计算机上从数据中产生“模型”的算法,即“学习算法…...

mysql 索引(为什么选择B+ Tree?)
索引实现原理 索引:排好序的数据结构 优点:降低I/O成本,CPU的资源消耗(数据持久化在磁盘中,每次查询都得与磁盘交互) 缺点:更新表效率变慢,(更新表数据,还要…...
蓝桥杯-带分数
法一 /* 再每一个a里去找c,他们共用一个st数组,可以解决重复出现数字 通过ac确定b,b不能出现<0 b出现的数不能和ac重复*/import java.util.Scanner;public class Main {static int n,res;static boolean[] st new boolean[15];static boolean[] backup new boolean[15];…...

消息队列面试题
目录 1. 为什么使用消息队列 2. 消息队列的缺点 3. 消息队列如何选型? 4. 如何保证消息队列是高可用的 5. 如何保证消息不被重复消费(见第二条) 6. 如何保证消息的可靠性传输? 7. 如何保证消息的顺序性(即消息幂…...

Android和IOS应用开发-Flutter 应用中实现记录和使用全局状态的几种方法
文章目录 在Flutter中记录和使用全局状态使用 Provider步骤1步骤2步骤3 使用 BLoC步骤1步骤2步骤3 使用 GetX:步骤1步骤2步骤3 在Flutter中记录和使用全局状态 在 Flutter 应用中,您可以使用以下几种方法来实现记录和使用全局状态,并在整个应…...

若依 ruoyi-cloud [网关异常处理]请求路径:/system/user/getInfo,异常信息:404
这里遇到的情况是因为nacos中的配置文件与项目启动时的编码不一样,若配置文件中有中文注释,那么用idea启动项目的时候,在参数中加上 -Dfile.encodingutf-8 ,保持编码一致,(用中文注释的配置文件,…...

自然语言处理里预训练模型——BERT
BERT,全称Bidirectional Encoder Representation from Transformers,是google在2018年提出的一个预训练语言模型,它的推出,一举刷新了当年多项NLP任务值的新高。前期我在零、自然语言处理开篇-CSDN博客 的符号向量化一文中简单介绍…...

2024年信息技术与计算机工程国际学术会议(ICITCEI 2024)
2024年信息技术与计算机工程国际学术会议(ICITCEI 2024) 2024 International Conference on Information Technology and Computer Engineering ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ 大会主题: 信息系统和技术…...
渗透测试修复笔记 - 02 Docker Remote API漏洞
需要保持 Docker 服务运行并且不希望影响其他使用 Docker 部署的服务,同时需要禁止外网访问特定的 Docker API 端口(2375):通过一下命令来看漏洞 docker -H tcp://ip地址:2375 images修改Docker配置以限制访问 修改daemon.json配…...
Spring(创建对象的方式3个)
3、Spring IOC创建对象方式一: 01、使用无参构造方法 //id:唯一标识 class:当前创建的对象的全局限定名 <bean id"us1" class"com.msb.pojo.User"/> 02、使用有参构造 <bean id"us2&…...

【GPT-SOVITS-02】GPT模块解析
说明:该系列文章从本人知乎账号迁入,主要原因是知乎图片附件过于模糊。 知乎专栏地址: 语音生成专栏 系列文章地址: 【GPT-SOVITS-01】源码梳理 【GPT-SOVITS-02】GPT模块解析 【GPT-SOVITS-03】SOVITS 模块-生成模型解析 【G…...

6个选品建议,改善你的亚马逊现状。
一、市场热点与需求调研 深入研究当前市场趋势,了解消费者需求的变化。使用亚马逊的销售数据、评价、问答等功能,以及第三方市场研究工具,比如店雷达,分析潜在热销产品的特点。注意季节性需求,提前布局相关选品&#…...
SQL中的SYSDATE函数
前言 在SQL语言中,SYSDATE 是一个非常实用且常见的系统内置函数,尤其在Oracle和MySQL数据库中广泛使用。它主要用来获取服务器当前的日期和时间,这对于进行实时数据记录、审计跟踪、有效期计算等场景特别有用。本文将详细解析SYSDATE函数的使…...
Rust的async和await支持多线程运行吗?
Rust的async和await的异步机制并不是仅在单线程下实现的,它们可以在多线程环境中工作,从而利用多核CPU的并行计算优势。然而,异步编程的主要目标之一是避免不必要的线程切换开销,因此,在单线程上下文中,asy…...

P2676 [USACO07DEC] Bookshelf B
[USACO07DEC] Bookshelf B 题目描述 Farmer John 最近为奶牛们的图书馆添置了一个巨大的书架,尽管它是如此的大,但它还是几乎瞬间就被各种各样的书塞满了。现在,只有书架的顶上还留有一点空间。 所有 N ( 1 ≤ N ≤ 20 , 000 ) N(1 \le N…...
在软件开发中正确使用MySQL日期时间类型的深度解析
在日常软件开发场景中,时间信息的存储是底层且核心的需求。从金融交易的精确记账时间、用户操作的行为日志,到供应链系统的物流节点时间戳,时间数据的准确性直接决定业务逻辑的可靠性。MySQL作为主流关系型数据库,其日期时间类型的…...
React 第五十五节 Router 中 useAsyncError的使用详解
前言 useAsyncError 是 React Router v6.4 引入的一个钩子,用于处理异步操作(如数据加载)中的错误。下面我将详细解释其用途并提供代码示例。 一、useAsyncError 用途 处理异步错误:捕获在 loader 或 action 中发生的异步错误替…...

云启出海,智联未来|阿里云网络「企业出海」系列客户沙龙上海站圆满落地
借阿里云中企出海大会的东风,以**「云启出海,智联未来|打造安全可靠的出海云网络引擎」为主题的阿里云企业出海客户沙龙云网络&安全专场于5.28日下午在上海顺利举办,现场吸引了来自携程、小红书、米哈游、哔哩哔哩、波克城市、…...

P3 QT项目----记事本(3.8)
3.8 记事本项目总结 项目源码 1.main.cpp #include "widget.h" #include <QApplication> int main(int argc, char *argv[]) {QApplication a(argc, argv);Widget w;w.show();return a.exec(); } 2.widget.cpp #include "widget.h" #include &q…...
【论文笔记】若干矿井粉尘检测算法概述
总的来说,传统机器学习、传统机器学习与深度学习的结合、LSTM等算法所需要的数据集来源于矿井传感器测量的粉尘浓度,通过建立回归模型来预测未来矿井的粉尘浓度。传统机器学习算法性能易受数据中极端值的影响。YOLO等计算机视觉算法所需要的数据集来源于…...
vue3 定时器-定义全局方法 vue+ts
1.创建ts文件 路径:src/utils/timer.ts 完整代码: import { onUnmounted } from vuetype TimerCallback (...args: any[]) > voidexport function useGlobalTimer() {const timers: Map<number, NodeJS.Timeout> new Map()// 创建定时器con…...

在 Spring Boot 中使用 JSP
jsp? 好多年没用了。重新整一下 还费了点时间,记录一下。 项目结构: pom: <?xml version"1.0" encoding"UTF-8"?> <project xmlns"http://maven.apache.org/POM/4.0.0" xmlns:xsi"http://ww…...
Kafka主题运维全指南:从基础配置到故障处理
#作者:张桐瑞 文章目录 主题日常管理1. 修改主题分区。2. 修改主题级别参数。3. 变更副本数。4. 修改主题限速。5.主题分区迁移。6. 常见主题错误处理常见错误1:主题删除失败。常见错误2:__consumer_offsets占用太多的磁盘。 主题日常管理 …...
Python 训练营打卡 Day 47
注意力热力图可视化 在day 46代码的基础上,对比不同卷积层热力图可视化的结果 import torch import torch.nn as nn import torch.optim as optim from torchvision import datasets, transforms from torch.utils.data import DataLoader import matplotlib.pypl…...

保姆级【快数学会Android端“动画“】+ 实现补间动画和逐帧动画!!!
目录 补间动画 1.创建资源文件夹 2.设置文件夹类型 3.创建.xml文件 4.样式设计 5.动画设置 6.动画的实现 内容拓展 7.在原基础上继续添加.xml文件 8.xml代码编写 (1)rotate_anim (2)scale_anim (3)translate_anim 9.MainActivity.java代码汇总 10.效果展示 逐帧…...