[计算机网络]---序列化和反序列化
前言
作者:小蜗牛向前冲
名言:我可以接受失败,但我不能接受放弃
如果觉的博主的文章还不错的话,还请
点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正
目录
一、再谈协议
二、序列化和反序化
1、网络版本计算器的场景搭建
2、服务器构建
3、客户端构建
4、序列化和反序列化
4.1自定义序列化和反序列化
4.2json实现序列化和反序列化
5、测试
本期学习:重点理解序化和反序列化
一、再谈协议
在前面博客谈网络的时候,我们认为协议就是一种约定,在用socket api接口的时候,都是按照“字符串”的方式来进行发送和接受的,但是如果我们要传输一些结构化的数据,又该怎么办?
这就要我们将数据序列化进行传输或者接收。
在网络通信中,传输结构化数据的常见方法有以下几种:
序列化: 将结构化数据转换为字符串或字节流进行传输。在发送端,可以使用一种序列化格式将数据转换为字符串或字节流;在接收端,再将接收到的字符串或字节流反序列化为相应的数据结构。常见的序列化格式包括 JSON(JavaScript Object Notation)、XML(eXtensible Markup Language)、Protocol Buffers(protobuf)、MessagePack 等。选择序列化格式时,需要考虑数据的大小、可读性、解析效率等因素。
数据格式协议: 使用一种规定的数据格式协议进行数据传输。这种方法通常需要双方预先约定好数据格式协议,并按照协议的规定进行数据的编码和解码。常见的数据格式协议包括 HTTP、FTP、SMTP 等。在 HTTP 协议中,可以使用 Content-Type 来指定数据的格式,如 application/json 表示 JSON 格式数据,application/xml 表示 XML 格式数据。
自定义协议: 自定义通信协议,定义数据的传输格式和规则。在自定义协议中,可以根据实际需求灵活地定义数据的结构和编码方式,以及通信过程中的规则和约定。自定义协议通常需要双方进行协商和实现,但可以更好地满足特定场景下的需求。
二、序列化和反序化
为了更好的理解,下面我们将通过自己定制tcp协议,将数据进行序列化和反序列化。为了方便叙述,我们简单的制定网络版本的计算 ,来理解协议中序列化和反序列化。
1、网络版本计算器的场景搭建
我们的构想是,客户端通过将计算请求数据序列化,发送到网络中,服务端接收后将数据进行反序列化进行计算处理。
处理过程大致如上图:
- c->s调用函数发送数据的本质就是将一个缓冲区的拷贝到另外一个缓冲区。
- s->c服务器回显数据的本质也是将s中缓冲区的数据拷贝到c中的缓冲区
- 所以所tcp是全双工的
2、服务器构建
这里构建的服务器和上篇在套接字中的是没有本质区别的
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <cstdlib>
#include <functional>
#include <unistd.h>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <sys/wait.h>
#include <signal.h>
#include "log.hpp"
#include "protocol.hpp"
using namespace std;namespace server
{enum{USAGE_ERR = 1,SOCKET_ERR,BIND_ERR,LISTEN_ERR};static const uint16_t gport = 8080;static const int gbacklog = 5;// const Request &req: 输入型// Response &resp: 输出型typedef std::function<bool(const Request &req, Response &resp)> func_t;// 保证解耦void handlerEntery(int sock, func_t func){std::string inbuffer;while (true){// 1. 读取:"content_len"\r\n"x op y"\r\n// 1.1 你怎么保证你读到的消息是 【一个】完整的请求std::string req_text, req_str;// 1.2 我们保证,我们req_text里面一定是一个完整的请求:"content_len"\r\n"x op y"\r\nif (!recPackage(sock, inbuffer, &req_text))return;std::cout << "带报头的请求:\n"<< req_text << std::endl;if (!deLength(req_text, &req_str))return;std::cout << "去掉报头的正文:\n"<< req_str << std::endl;// 2. 对请求Request,反序列化// 2.1 得到一个结构化的请求对象Request req;if (!req.deserialize(req_str))return;// 3. 计算机处理,req.x, req.op, req.y --- 业务逻辑// 3.1 得到一个结构化的响应Response resp;func(req, resp); // req的处理结果,全部放入到了resp, 回调是不是不回来了?不是!// 4.对响应Response,进行序列化// 4.1 得到了一个"字符串"std::string resp_str;resp.serialize(&resp_str);std::cout << "计算完成, 序列化响应: " << resp_str << std::endl;// 5. 然后我们在发送响应// 5.1 构建成为一个完整的报文std::string send_string = enLength(resp_str);send(sock, send_string.c_str(), send_string.size(), 0); // 其实这里的发送也是有问题的,不过后面再说}}class CalServer{public:CalServer(const uint16_t &port = gport) : _listensock(-1), _port(port){}void initServer(){// 1. 创建socket文件套接字对象_listensock = socket(AF_INET, SOCK_STREAM, 0);if (_listensock < 0){logMessage(FATAL, "create socket error");exit(SOCKET_ERR);}logMessage(NORMAL, "create socket success: %d", _listensock);// 2. bind绑定自己的网络信息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(_listensock, (struct sockaddr *)&local, sizeof(local)) < 0){logMessage(FATAL, "bind socket error");exit(BIND_ERR);}logMessage(NORMAL, "bind socket success");// 3. 设置socket 为监听状态if (listen(_listensock, gbacklog) < 0) // 第二个参数backlog后面在填这个坑{logMessage(FATAL, "listen socket error");exit(LISTEN_ERR);}logMessage(NORMAL, "listen socket success");}void start(func_t func){for (;;){// 4. server 获取新链接// sock, 和client进行通信的fdstruct sockaddr_in peer;socklen_t len = sizeof(peer);int sock = accept(_listensock, (struct sockaddr *)&peer, &len);if (sock < 0){logMessage(ERROR, "accept error, next");continue;}logMessage(NORMAL, "accept a new link success, get new sock: %d", sock); // ?// version 2 多进程版(2)pid_t id = fork();if (id == 0) // child{close(_listensock);// if(fork()>0) exit(0);// serviceIO(sock);handlerEntery(sock, func);close(sock);exit(0);}close(sock);// fatherpid_t ret = waitpid(id, nullptr, 0);if (ret > 0){logMessage(NORMAL, "wait child success"); // ?}}}~CalServer() {}private:int _listensock; // 不是用来进行数据通信的,它是用来监听链接到来,获取新链接的!uint16_t _port;};
}
对于 handlerEntery这个操作我们要分析,在这函数中首先我们要获取到由客户端发送过来的报文,而客户端发送过来的报文肯定是经过序列化的,所以我们要进行反序列化,在通过func回调函数得到一个结构化的响应,在将响应通过处理发送给客户端。
// 保证解耦void handlerEntery(int sock, func_t func){std::string inbuffer;while (true){// 1. 读取:"content_len"\r\n"x op y"\r\n// 1.1 你怎么保证你读到的消息是 【一个】完整的请求std::string req_text, req_str;// 1.2 我们保证,我们req_text里面一定是一个完整的请求:"content_len"\r\n"x op y"\r\nif (!recPackage(sock, inbuffer, &req_text))return;std::cout << "带报头的请求:\n"<< req_text << std::endl;if (!deLength(req_text, &req_str))return;std::cout << "去掉报头的正文:\n"<< req_str << std::endl;// 2. 对请求Request,反序列化// 2.1 得到一个结构化的请求对象Request req;if (!req.deserialize(req_str))return;// 3. 计算机处理,req.x, req.op, req.y --- 业务逻辑// 3.1 得到一个结构化的响应Response resp;func(req, resp); // req的处理结果,全部放入到了resp, 回调是不是不回来了?不是!// 4.对响应Response,进行序列化// 4.1 得到了一个"字符串"std::string resp_str;resp.serialize(&resp_str);std::cout << "计算完成, 序列化响应: " << resp_str << std::endl;// 5. 然后我们在发送响应// 5.1 构建成为一个完整的报文std::string send_string = enLength(resp_str);send(sock, send_string.c_str(), send_string.size(), 0); // 其实这里的发送也是有问题的,不过后面再说}}
对于calServer.cc的主程序,就是简单进行cal计算业务
#include "calServer.hpp"
#include <memory>using namespace server;
using namespace std;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " local_port\n\n";
}
// req: 里面一定是我们的处理好的一个完整的请求对象
// resp: 根据req,进行业务处理,填充resp,不用管理任何读取和写入,序列化和反序列化等任何细节
bool cal(const Request &req, Response &resp)
{// req已经有结构化完成的数据啦,你可以直接使用resp._exitcode = OK;resp._result = OK;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._exitcode = DIV_ZERO;elseresp._result = req._x / req._y;}break;case '%':{if (req._y == 0)resp._exitcode = MOD_ZERO;elseresp._result = req._x % req._y;}break;default:resp._exitcode = OP_ERROR;break;}return true;
}// tcp服务器,启动上和udp server一模一样
// ./tcpserver local_port
int main(int argc, char *argv[])
{if (argc != 2){Usage(argv[0]);exit(USAGE_ERR);}uint16_t port = atoi(argv[1]);unique_ptr<CalServer> tsvr(new CalServer(port));tsvr->initServer();tsvr->start(cal);return 0;
}
3、客户端构建
对于客户端calClinet.hpp完成的主要逻辑是进行连网,输入计算,对服务器进行计算请求,在进行计算信息的构建。
#pragma once#include <iostream>
#include <string>
#include <cstring>
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include "protocol.hpp"#define NUM 1024
using namespace std;
class CalClient
{
public:CalClient(const std::string &serverip, const uint16_t &serverport): _sock(-1), _serverip(serverip), _serverport(serverport){}void initClient(){// 1. 创建socket_sock = socket(AF_INET, SOCK_STREAM, 0);if (_sock < 0){std::cerr << "socket create error" << std::endl;exit(2);}// 2. tcp的客户端要不要bind?要的! 要不要显示的bind?不要!这里尤其是client port要让OS自定随机指定!// 3. 要不要listen?不要!// 4. 要不要accept? 不要!// 5. 要什么呢??要发起链接!}void start(){struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(_serverport);server.sin_addr.s_addr = inet_addr(_serverip.c_str());if (connect(_sock, (struct sockaddr *)&server, sizeof(server)) != 0){std::cerr << "socket connect error" << std::endl;}else{std::string line;std::string inbuffer;while (true){std::cout << "mycal>>> ";// 输入计算getline(cin, line);// 进行请求Request req = ParseLine(line);string content; // 存放计算信息req.serialize(&content);string send_string = enLength(content); // 添加报头send(_sock, send_string.c_str(), send_string.size(), 0);string package, text;if (!recPackage(_sock, inbuffer, &package))continue;if (!deLength(package, &text))continue;// 响应Response resp;resp.deserialize(text);std::cout << "exitCode: " << resp._exitcode << std::endl;std::cout << "result: " << resp._result << std::endl;}}}// 从文本中提取计算格式信息,然后用这些信息构建请求Request ParseLine(const std::string &line){//"1+1" "123*456" "12/0"int status = 0; // 0:操作符之前,1:碰到了操作符 2:操作符之后int i = 0;int cnt = line.size();string left, right;char op;while (i < cnt){switch (status){case 0:{if (!isdigit(line[i])) // isdigit检查字符是否是十进制{op = line[i];status = 1;}elseleft.push_back(line[i++]);}break;case 1:{i++;status = 2;}break;case 2:{right.push_back(line[i++]);}break;}}std::cout << std::stoi(left) << " " << std::stoi(right) << " " << op << std::endl;return Request(std::stoi(left), std::stoi(right), op);}~CalClient(){if (_sock >= 0)close(_sock);}private:int _sock;std::string _serverip;uint16_t _serverport;
};
calClient.cc
#include "calClient.hpp"
#include <memory>using namespace std;static void Usage(string proc)
{cout << "\nUsage:\n\t" << proc << " serverip serverport\n\n";
}
// ./tcpclient serverip serverport
int main(int argc, char *argv[])
{if (argc != 3){Usage(argv[0]);exit(1);}string serverip = argv[1];uint16_t serverport = atoi(argv[2]);unique_ptr<CalClient> tcli(new CalClient(serverip, serverport));tcli->initClient();tcli->start();return 0;
}
4、序列化和反序列化
4.1自定义序列化和反序列化
前面说了一大堆要对数据进行序列化和反序列化 ,那到底什么是序列化和反序列,其实本质就是将一堆字符串,整和成一个字符串。为了完成我们网络计算器,这里我们写了二个类,一个是 Request对数据进行请求,另外一个是Response,每个类中都要对序化和反序列进行设计
class Request
{
public:Request() : _x(0), _y(0), _op(0){}Request(int x, int y, char op) : _x(x), _y(y), _op(op){}// 1. 自己写// 2. 用现成的bool serialize(std::string *out){}// "x op yyyy";bool deserialize(const std::string &in){}
public:// "x op y"int _x;int _y;char _op;
};
// 响应
class Response
{
public:Response() : _exitcode(0), _result(0){}Response(int exitcode, int result) : _exitcode(exitcode), _result(result){}bool serialize(std::string *out){}bool deserialize(const std::string &in){}public:int _exitcode; // 0:计算成功,!0表示计算失败,具体是多少,定好标准int _result; // 计算结果
};
其中序列化和反序列化可以自己进行编写,也可以通过库进行完成。
在大部分场景中我们都是用json进行序列结构的,对于自定义序列化,每个实现方式可能不同,所以不重点分析了,下面会有完整代码,大家可以参考实现
4.2json实现序列化和反序列化
在Linux上使用json我们要进行库的安装
sudo yum install -y jsoncpp-devel
序列化
使用了 JsonCpp 库来创建一个 JSON 对象 root
,然后将一些值 _x
、_y
和 _op
分别存储到 JSON 对象的键 "first"
、"second"
和 "oper"
中。接下来,使用 Json::FastWriter
对象 writer
来将 JSON 对象 root
转换为字符串,并将结果写入到 out
指向的位置。
Json::Value root;root["first"] = _x;root["second"] = _y;root["oper"] = _op;Json::FastWriter writer;// Json::StyledWriter writer;*out = writer.write(root);
反序列化
使用了 JsonCpp 库来解析输入的 JSON 字符串 in
,并将解析得到的值存储到 Json::Value
类型的对象 root
中。然后,通过访问 root
对象的键 "first"
、"second"
和 "oper"
来获取相应的值,并将这些值转换为整数类型,并分别存储到 _x
、_y
和 _op
变量中。
Json::Value root;Json::Reader reader;reader.parse(in, root);_x = root["first"].asInt();_y = root["second"].asInt();_op = root["oper"].asInt();
注意:asInt()
函数用于将 JSON 值转换为整数类型
protocol.hpp 完整代码实现:添加报文,去报头,请求,响应获取数据包
#pragma once#include <iostream>
#include <cstring>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <jsoncpp/json/json.h>#define SEP " "
#define SEP_LEN strlen(SEP)
#define LINE_SEP "/r/n" // /r回车 /n换行 /r/n表示换行的同时将光标移动到行首
#define LINE_SEP_LEN strlen(LINE_SEP)
using namespace std;// 错误枚举
enum
{OK = 0,DIV_ZERO,MOD_ZERO,OP_ERROR
};
// "x op y" -> "content_len"\r\n"x op y"\r\n
// "exitcode result" -> "content_len"\r\n"exitcode result"\r\n// 添加报头
string enLength(string &text)
{string send_string = to_string(text.size());send_string += LINE_SEP;send_string += text;send_string += LINE_SEP;return send_string;
}
// 去报头
// "content_len"\r\n"exitcode result"\r\n
bool deLength(const std::string &package, std::string *text)
{auto pos = package.find(LINE_SEP);if (pos == string::npos)return false;string text_len_string = package.substr(0, pos);int text_len = stoi(text_len_string);*text = package.substr(pos + LINE_SEP_LEN, text_len);return true;
}// 请求
class Request
{
public:Request() : _x(0), _y(0), _op(0){}Request(int x, int y, char op) : _x(x), _y(y), _op(op){}// 1. 自己写// 2. 用现成的bool serialize(std::string *out){
#ifdef MYSELF*out = "";// 结构化 -> "x op y";std::string x_string = std::to_string(_x);std::string y_string = std::to_string(_y);*out = x_string;*out += SEP;*out += _op;*out += SEP;*out += y_string;
#elseJson::Value root;root["first"] = _x;root["second"] = _y;root["oper"] = _op;Json::FastWriter writer;// Json::StyledWriter writer;*out = writer.write(root);
#endifreturn true;}// "x op yyyy";bool deserialize(const std::string &in){
#ifdef MYSELF// "x op y" -> 结构化auto left = in.find(SEP);auto right = in.rfind(SEP);if (left == std::string::npos || right == std::string::npos)return false;if (left == right)return false;if (right - (left + SEP_LEN) != 1)return false;std::string x_string = in.substr(0, left); // [0, 2) [start, end) , start, end - startstd::string y_string = in.substr(right + SEP_LEN);if (x_string.empty())return false;if (y_string.empty())return false;_x = std::stoi(x_string);_y = std::stoi(y_string);_op = in[left + SEP_LEN];
#elseJson::Value root;Json::Reader reader;reader.parse(in, root);_x = root["first"].asInt();_y = root["second"].asInt();_op = root["oper"].asInt();
#endifreturn true;}public:// "x op y"int _x;int _y;char _op;
};// 响应
class Response
{
public:Response() : _exitcode(0), _result(0){}Response(int exitcode, int result) : _exitcode(exitcode), _result(result){}bool serialize(std::string *out){
#ifdef MYSELF*out = "";std::string ec_string = std::to_string(_exitcode);std::string res_string = std::to_string(_result);*out = ec_string;*out += SEP;*out += res_string;
#elseJson::Value root;root["exitcode"] = _exitcode;root["result"] = _result;Json::FastWriter writer;*out = writer.write(root);
#endifreturn true;}bool deserialize(const std::string &in){
#ifdef MYSELF// "exitcode result"auto mid = in.find(SEP);if (mid == std::string::npos)return false;std::string ec_string = in.substr(0, mid);std::string res_string = in.substr(mid + SEP_LEN);if (ec_string.empty() || res_string.empty())return false;_exitcode = std::stoi(ec_string);_result = std::stoi(res_string);
#elseJson::Value root;Json::Reader reader;reader.parse(in, root);_exitcode = root["exitcode"].asInt();_result = root["result"].asInt();
#endifreturn true;}public:int _exitcode; // 0:计算成功,!0表示计算失败,具体是多少,定好标准int _result; // 计算结果
};// 获取数据包
// "content_len"\r\n"x op y"\r\n"content_len"\r\n"x op y"\r\n"content_len"\r\n"x op
bool recPackage(int sock, string &inbuffer, string *text)
{char buffer[1024];while (true){// 从网络中接受资源ssize_t n = recv(sock, buffer, sizeof(buffer) - 1, 0);if (n > 0){buffer[n] = 0;inbuffer += buffer;// 分析auto pos = inbuffer.find(LINE_SEP);if (pos == string::npos)continue;string text_len_string = inbuffer.substr(0, pos);int text_len = stoi(text_len_string);// 报文的总长度int total_len = text_len_string.size() + 2 * LINE_SEP_LEN + text_len;// text_len_string + "\r\n" + text + "\r\n" <= inbuffer.size();std::cout << "处理前#inbuffer: \n"<< inbuffer << std::endl;if (inbuffer.size() < total_len){std::cout << "你输入的消息,没有严格遵守我们的协议,正在等待后续的内容, continue" << std::endl;continue;}// 最少一个完整报文*text = inbuffer.substr(0, total_len);inbuffer.erase(0, total_len);std::cout << "处理后#inbuffer:\n " << inbuffer << std::endl;break;}elsereturn false;}return true;
}
5、测试
下面我们进行几组简单的测试:
左边是服务器,又边上是客户端
Linux小命令
killall
是一个在 Unix 和类 Unix 操作系统上用于终止进程的命令。与 kill
命令不同,killall
不是根据进程 ID(PID)来终止进程,而是根据进程的名称来匹配并终止相应的进程。
killall [选项] 进程名
其中,选项可以用来指定不同的操作行为,而进程名则是要终止的进程的名称。
一些常用的选项包括:
-e
:显示详细的错误信息。-i
:交互模式,在终止进程之前询问用户。-q
:安静模式,不显示任何输出。-u
:指定用户,仅终止指定用户的进程。
注意:killall
命令会终止所有匹配名称的进程,因此需要谨慎使用,以免意外终止系统中重要的进程
相关文章:

[计算机网络]---序列化和反序列化
前言 作者:小蜗牛向前冲 名言:我可以接受失败,但我不能接受放弃 如果觉的博主的文章还不错的话,还请点赞,收藏,关注👀支持博主。如果发现有问题的地方欢迎❀大家在评论区指正 目录 一、再谈协议…...
[前端开发] 常见的 HTML CSS JavaScript 事件
代码示例指路 常见的 HTML、CSS、JavaScript 事件代码示例 常见的 HTML CSS JavaScript 事件 事件HTML 事件鼠标事件键盘事件表单事件 JavaScript 事件对象事件代理(事件委托) 事件 在 Web 开发中,事件是用户与网页交互的重要方式之一。通过…...
H5/CSS 笔试面试考题(71-80)
简述哪种输入类型用于定义周和年控件(无时区)( ) A:date B:week C:year 面试通过率:67.0% 推荐指数: ★★★★★ 试题难度: 初级 试题类型: 选择题 答案:b 简述下列哪个元素表示外部资源?该元素可以被视为图像、嵌套的浏览上下文或插件要处理的资源。它包括各种属性…...

【Node.js】path 模块进行路径处理
Node.js 执行 JS 代码时,代码中的路径都是以终端所在文件夹出发查找相对路径,而不是以我们认为的从代码本身出发,会遇到问题,所以在 Node.js 要执行的代码中,访问其他文件,建议使用绝对路径 实例࿱…...

react+ts【项目实战一】配置项目/路由/redux
文章目录 1、项目搭建1、创建项目1.2 配置项目1.2.1 更换icon1.2.2 更换项目名称1.2.1 配置项目别名 1.3 代码规范1.3.1 集成editorconfig配置1.3.2 使用prettier工具 1.4 项目结构1.5 对css进行重置1.6 注入router1.7 定义TS组件的规范1.8 创建代码片段1.9 二级路由和懒加载1.…...

英文论文(sci)解读复现【NO.20】TPH-YOLOv5++:增强捕获无人机的目标检测跨层不对称变压器的场景
此前出了目标检测算法改进专栏,但是对于应用于什么场景,需要什么改进方法对应与自己的应用场景有效果,并且多少改进点能发什么水平的文章,为解决大家的困惑,此系列文章旨在给大家解读发表高水平学术期刊中的 SCI论文&a…...
第十五章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性
文章目录 第十五章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性FetchRows()GatewayStatus propertyGatewayStatusGet()GetConnection()GetGTWVersion()GetLastSQLCode() 第十五章 以编程方式使用 SQL 网关 - %SQLGatewayConnection 方法和属性 FetchRows() …...
【QTableView】
QTableView是Qt框架中用于显示表格形式数据的部件,通常用于显示数据库查询结果、数据集以及其他类似的结构化数据。 以下是一个使用QTableView的简单示例,假设我们有一个数据库表存储了学生的信息,我们可以使用QSqlTableModel将数据库表关联到QTableView上,并显示出来: …...
VS-Code-C#配置
C#开发环境配置 查看更多学习笔记:GitHub:LoveEmiliaForever 1. 安装 .NET SDK 官方下载网址按照安装程序指引安装即可 2. VS Code 安装插件 插件名:C#发布者是Microsoft 该插件是基础语法插件 插件名:C# Dev Kit发布者是Mic…...

第七篇【传奇开心果系列】Python微项目技术点案例示例:数据可视化界面图形化经典案例
传奇开心果微博系列 系列微博目录Python微项目技术点案例示例系列 微博目录一、微项目开发背景和项目目标:二、雏形示例代码三、扩展思路介绍四、数据输入示例代码五、数据分析示例代码六、排名统计示例代码七、数据导入导出示例代码八、主题定制示例代码九、数据过…...
LeetCode 第33天 | 1005. K 次取反后最大化的数组和 135. 分发糖果 134. 加油站
1005. K 次取反后最大化的数组和 按照绝对值大小降序排序,然后将负值变正,如果所有负值都正了,但是还有k余量且为奇数,那就将绝对值最小值(最后一个元素)取反,否则直接结束。 class Solution {…...
PointMixer论文阅读笔记
MLP-mixer是最近很流行的一种网络结构,比起Transformer和CNN的节构笨重,MLP-mixer不仅节构简单,而且在图像识别方面表现优异。但是MLP-mixer在点云识别方面表现欠佳,PointMixer就是在保留了MLP-mixer优点的同时,还可以…...

[word] word分割线在哪里设置 #其他#经验分享
word分割线在哪里设置 在工作中有些技巧,可以快速提高工作效率,解决大部分工作,今天给大家分享word分割线在哪里设置的小技能,希望可以帮助到你。 1、快速输入分割线 输入三个【_】按下回车就是一条长直线,同样分别…...

C++ 音视频原理
本篇文章我们来描述一下音视频原理 音视频录制原理: 下面是对这张思维导图的介绍 摄像头部分: 麦克风采集声音 摄像头采集画面 摄像头采集回来的数据可以用RGB也可以用YUV来表示 图像帧帧率 一秒能处理多少张图像 图像处理 :调亮度 图像帧队列 :意思是将数据取…...
C# 只允许开启一个exe程序
C# 只允许开启一个exe程序 第一种方法 电脑只能启动一次再次点击显示当前exe程序 using System; using System.Collections.Generic; using System.Diagnostics; using System.Linq; using System.Runtime.InteropServices; using System.Threading.Tasks; using System.Win…...

【Java程序员面试专栏 分布式中间件】Redis 核心面试指引
关于Redis部分的核心知识进行一网打尽,包括Redis的基本概念,基本架构,工作流程,存储机制等,通过一篇文章串联面试重点,并且帮助加强日常基础知识的理解,全局思维导图如下所示 基础概念 明确redis的特性、应用场景和数据结构 什么是Redis,Redis有哪些应用场景 Redi…...

2024年【高处安装、维护、拆除】模拟考试题库及高处安装、维护、拆除实操考试视频
题库来源:安全生产模拟考试一点通公众号小程序 高处安装、维护、拆除模拟考试题库是安全生产模拟考试一点通生成的,高处安装、维护、拆除证模拟考试题库是根据高处安装、维护、拆除最新版教材汇编出高处安装、维护、拆除仿真模拟考试。2024年【高处安装…...
【QT+QGIS跨平台编译】之三十七:【Shapelib+Qt跨平台编译】(一套代码、一套框架,跨平台编译)
文章目录 一、Shapelib介绍二、Shapelib下载三、文件分析四、pro文件五、编译实践一、Shapelib介绍 Shapelib是一个开源的C库,用于读取、写入和操作ESRI Shapefile格式的地理矢量数据。 ESRI Shapefile是一种常见的地理信息系统(GIS)文件格式,用于存储地理矢量数据,包括…...

【机器学习基础】决策树(Decision Tree)
🚀个人主页:为梦而生~ 关注我一起学习吧! 💡专栏:机器学习 欢迎订阅!后面的内容会越来越有意思~ ⭐特别提醒:针对机器学习,特别开始专栏:机器学习python实战 欢迎订阅&am…...
图神经网络DGL框架,graph classification,多个且不同维度的node feature 训练
node feature 维度不同 我现在有许多不同的图要加入训练,每个图的节点特征维度不同,第一张图n_weight特征有10条数据,第二张图n_weight特征有15条数据,但是训练的时候,需要维度都对其,所以直接做0 padding…...
蓝桥杯(Web大学组)2022国赛真题:用什么来做计算 A
判分标准 实现重置(AC)功能,得 1 分。 实现计算式子和结果显示功能,得 3 分。 实现计算功能,得 6 分。 应该按要求来就行吧,,一开始还在想是否要考虑小数点个数的问题还有式子是否有效…… 笔记…...

Linux POSIX信号量 线程池
Linux POSIX信号量 线程池 一. 什么是POSIX信号量?二. POSIX信号量实现原理三. POSIX信号量接口函数四. 基于环形队列的生产消费模型五. 线程池 一. 什么是POSIX信号量? POSIX信号量是一种用于同步和互斥操作的机制,属于POSIX(Po…...

Sentinel(理论版)
Sentinel 1.什么是Sentinel Sentinel 是一个开源的流量控制组件,它主要用于在分布式系统中实现稳定性与可靠性,如流量控制、熔断降级、系统负载保护等功能。简单来说,Sentinel 就像是一个交通警察,它可以根据系统的实时流量&…...

python3 获取某个文件夹所有的pdf文件表格提取表格并一起合并到excel文件
下面是一个完整的示例,其中包括了merge_tables_to_excel函数的定义,并且假设该函数的功能是从每个PDF文件中提取第一个表格并将其合并到一个Excel文件中: import os from pathlib import Path import pandas as pd import pdfplumber …...

【AIGC】Stable Diffusion的模型入门
下载好相关模型文件后,直接放入Stable Diffusion相关目录即可使用,Stable Diffusion 模型就是我们日常所说的大模型,下载后放入**\webui\models\Stable-diffusion**目录,界面上就会展示相应的模型选项,如下图所示。作者…...

【JavaEE】_HTTP请求首行详情
目录 1. URL 2. 方法 2.1 GET方法 2.2 POST方法 2.3 GET与POST的区别 2.4 低频使用方法 1. URL 在mysql JDBC中已经提到过URL的相关概念: 如需查看有关JDBC更多内容,原文链接如下: 【MySQL】_JDBC编程-CSDN博客 URL用于描述某个资源…...

Linux第48步_编译正点原子的出厂Linux内核源码
编译正点原子的出厂 Linux 内核源码,为后面移植linux做准备。研究对象如下: 1)、linux内核镜像文件“uImage” 路径为“arch/arm/boot”; 2)、设备树文件“stm32mp157d-atk.dtb” 路径为“arch/arm/boot/dts” 3)、默认配置文件“stm32m…...

程序员为什么不喜欢关电脑?
程序员为什么不喜欢关电脑? 本人40 最近待业。,希望 3月前能再就业吧!就不喜欢关电脑 这个问题来说是不好习惯。毕竟你的电脑不是服务器,哈哈。但是程序员都很懒,能自动化的,就让机器干。我在此之前 也工作…...

【初始RabbitMQ】了解和安装RabbitMQ
RabbitMQ的概念 RabbitMQ是一个消息中间件:他可以接受并转发消息。例如你可以把它当做一个快递站点,当你要发送一个包 裹时,你把你的包裹放到快递站,快递员最终会把你的快递送到收件人那里,按照这种逻辑 RabbitMQ 是 …...

Linux第56步_根文件系统第3步_将busybox构建的根文件系统烧录到EMMC
1、第1次将“rootfs”打包 1)、打开第1个终端,准备在“mnt”目录下创建挂载目录“rootfs”; 输入“ls回车” 输入“cd /mnt回车” 输入“ls回车”,查看“mnt”目录下的文件和文件夹 输入“sudo mkdir rootfs回车”,在“mnt”…...