49 序列化和反序列化
本章重点
理解应用层的作用,初识http协议
理解传输层的作用,深入理解tcp的各项特性和机制
对整个tcp/ip协议有系统的理解
对tcp/ip协议体系下的其他重要协议和技术有一定的了解
学会使用一些网络问题的工具和方法
目录
1.应用层
2.协议概念
3. 网络计算器
4. 序列化和反序列化
5. 协议定制
6. 数据处理
7. 网络函数封装
8. 服务端
9. 客户端
10.结果示例
11. json序列化
12. 添加条件选项
13.再看七层模型
1. 应用层
实际解决问题,满足日常需求的网络程序都在应用层
2. 协议概念
协议是一种“约定”,socket api的接口,在读写数据时,都是按“字符串”的方式来发送接收的,如果我们要传输一些结构化的数据,怎么办?
tcp也称作传输控制协议(什么时候发,发多少,出错了怎么办),传输层是在os内部实现的,是os网络模块部分,将数据交给tcp实际上就是交给os,由于os决定数据的发送,那么收上来的数据就不能完全确定了,有可能是完整的,也有可能是多个报文,或者一部分。所以为了成功的发送和解析报文,应用层就需要协议约定好数据的格式,确定数据的完整性,如果长度不符,就不处理
3. 网络计算器
需要实现一个服务器的计算器,把客户端两个数发过去,然后由服务器计算,最后把结果返回给客户端
约定方案
约定方案一:
客户端发送一个形如“1+1”的字符串
这个字符串有两个操作数,都是整形
两个数字之间会有一个字符是运算符
数字和运算符之间没有空格
。。。
这种情况如果一次性发送了四五组数据,无法区分是一个还是几个报文
约定方案二:
定义结构体表示需要交互的信息
发送数据时将这个结构体按照一个规则转换成字符串,接收到数据的时候再按照相同的规则将把字符串串转换回结构体
// proto.h 定义通信的结构体
typedef struct Request {int a;int b;
} Request;
typedef struct Response {int sum;
} Response;
// client.c 客户端核心代码
Request request;
Response response; scanf("%d,%d", &request.a, &request.b);
write(fd, request, sizeof(Request));
read(fd, response, sizeof(Response));
// server.c 服务端核心代码
Request request;
read(client_fd, &request, sizeof(request));
Response response;
response.sum = request.a + request.b;
write(client_fd, &response, sizeof(response));
直接发送结构体,结构体两边类型一样,这种方法可以,但是不同的设备结构体的对齐方式可能不一样,导致同一个结构体大小不一样。出问题后非常难调试,都是二进制的。如果一出性发好多个,也不好区分一个个报文,不过os内部是这样实现的,各种情况都考虑了
只要保证一端发送,另一端能够正确解析,这种约定就是应用层协议。为了更稳定,可以定下面的约定
4. 序列化和反序列化
上面的约定方式都有不足之处。最终的约定方式以qq消息举例,需要的结构体包含消息内容,昵称,发送时间,将这三个字符串组合为一个字符串发送给客户,客户收到后又重新转换为结构体,解析为发送时间+昵称+内容的结构,双方的内容结构是相同的。也实现了简单的分层,上面负责结构的规划组成序列,下面负责将序列化的数据完整发送和接收,为了方便解析和发送,还需要规定报文之间的分隔。这样序列化和反序列化方便网络收发
5. 协议定制
协议分两个类,一个是请求类,一个是响应类,请求方用计算类,生成计算式,两个操作数一个操作符,所以成员变量两个int为操作数,一个char操作符。将数据序列化为“x 操作符 y”的结构发送,对方收到后还要提供反序列化为计算类来计算结果
结算结果放到结果类,两个成员变量,一个是int的结果,一个是int的返回码,表明结果可不可信。也要提供序列化和反序列化功能,将结果和返回码改变为“结果 返回码”的字符串格式
上面的数据可以用来发送了,但如果客户一次性发了很多个数据,或者一个报文也不满足。如何区分每个报文?所以必须为发出的数据添加报头,报头格式定为“长度\n报文\n”,有添加报头也要解析报头
protocol.hpp
#pragma once
#include <string>//分隔符
const std::string black_sep = " ";
const std::string protocol_sep = "\n";//解决报文外部格式//len\n正文\nstd::string encode(std::string& message){std::string package = std::to_string(message.size());package += protocol_sep;package += message;package += protocol_sep;return package;}//len\na + b\nbool decode(std::string& message, std::string* content){std::size_t pos = message.find(protocol_sep);if (pos == std::string::npos){return false;}std::string len_str = message.substr(0, pos);std::size_t len = std::stoi(len_str);std::size_t total_len = len_str.size() + len + 2;//检查长度if (message.size() < total_len){return false;}*content = message.substr(pos + 1, len);//earse 移除报文message.erase(0, total_len);return true;}class Request
{
public:Request(){}Request(int a, int b, char oper){_num1 = a;_num2 = b;_op = oper;}//a + bbool serialize(std::string* out){//构建报文有效载荷std::string str;str += std::to_string(_num1);str += black_sep;str += _op;str += black_sep;str += std::to_string(_num2);*out = str;return true;}//a + bbool deserialize(std::string& in){//astd::size_t left = in.find(black_sep);if (left == std::string::npos){return false; }std::string part_a = in.substr(0, left);// bstd::size_t right = in.rfind(black_sep);if (right == std::string::npos){return false; }std::string part_b = in.substr(right + 1);//+if (left + 2 != right){return false;}_op = in[left+1];_num1 = std::stoi(part_a);_num2 = std::stoi(part_b);return true;}void debugprint(){cout << "新请求构建完成:" << _num1 << _op << _num2 << endl;}public:int _num1;int _num2;char _op;
};class Response
{
public:Response(){}Response(int res, int cod){_result = res;_code = cod;}//1000 0bool serialize(std::string* out){string str = std::to_string(_result);str += black_sep;str += std::to_string(_code);*out = str;return true;}//1000 0bool deserialize(std::string& in){std::size_t pos = in.find(black_sep);if (pos == std::string::npos){return false;}std::string left = in.substr(0, pos);std::string right = in.substr(pos + 1);_result = std::stoi(left);_code = std::stoi(right);return true;}void debugprint(){cout << "结果响应完成,result:" << _result << ",code:" << _code << endl;}public:int _result;int _code; //0可信,否则表明对应的错误
};
6. 数据处理
有了协议就可以实现数据处理,计算结果并封装的类
枚举各种计算错误的情况,操作符等其他问题用OTHER
计算函数传入上面的请求类,返回结果响应类。根据操作符进行不同的运算
数据处理函数将收到的字符串内容转换为请求类,调用计算函数得到结果,并对结果封包返回字符串用来发送
servercal.hpp
#pragma once
#include "protocol.hpp"enum
{DIVZERO = 1,MODZERO,OTHER_OPER
};class ServerCal
{
public:ServerCal(){}Response CalculatorHelp(const Request& req){Response res(0, 0);switch (req._op){case '+':res._result = req._num1 + req._num2;break;case '-':res._result = req._num1 - req._num2;break;case '*':res._result = req._num1 * req._num2;break;case '/':if (req._num2 == 0){res._code = DIVZERO;}else{res._result = req._num1 / req._num2;}break;case '%':if (req._num2 == 0){res._code = MODZERO;}else{res._result = req._num1 % req._num2;break;}default:res._code = OTHER_OPER;break;}return res;}std::string Calcluator(std::string& package){std::string content;bool r = decode(package, &content);if (!r){return "";}Request req;r = req.deserialize(content);if (!r){return "";}req.debugprint();content = "";Response res = CalculatorHelp(req);res.debugprint();res.serialize(&content);content = encode(content); // len\n正文\nreturn content;}~ServerCal(){}
};
7. 网络函数封装
将服务器的socket常用功能封装为scoke类,成员sockfd为socket函数的返回值,提供返回sockfd的函数
Socket.hpp
#pragma once
#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>
#include <stdlib.h>
#include "log.hpp"enum
{SOCKERR = 1,BINDERR,LISERR
};Log lg;
const int backlog = 5;
class Sock
{
public:Sock(){}void Socket(){_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){lg.logmessage(fatal, "socket error");exit(SOCKERR);}}void Bind(uint16_t port){struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_addr.s_addr = INADDR_ANY;local.sin_port = htons(port);int bret = bind(_sockfd, (const struct sockaddr*)&local, sizeof(local));if (bret < 0){lg.logmessage(fatal, "bind error");exit(BINDERR);}}void Listen(){int lret = listen(_sockfd, backlog);if (lret < 0){lg.logmessage(fatal, "listen error");exit(LISERR);}}int Accept(string* clientip, uint16_t* clientport){sockaddr_in peer;socklen_t len = sizeof(peer);int newfd = accept(_sockfd, (sockaddr*)&peer, &len);if (newfd < 0){lg.logmessage(warning, "accept error");return -1;}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 string ip, const uint16_t port){sockaddr_in peer;memset(&peer, 0, sizeof(peer));peer.sin_family = AF_INET;inet_pton(AF_INET, ip.c_str(), &peer.sin_addr);peer.sin_port = htons(port);int cret = connect(_sockfd, (const struct sockaddr*)&peer, sizeof(peer));if (cret == -1){lg.logmessage(warning, "connect error");return false;}return true;}void Close(){close(_sockfd);}int Fd(){return _sockfd;}~Sock(){}
public:int _sockfd;
};
8. 服务端
服务端和通用服务器一样,accept收到连接请求后,创建子进程提供服务,因为数据可能不是一次性接收完的,所以recbuff不断加上读到的内容。同时有可能有多个报文,所以收到数据后进入循环处理,将字符串交给函数模板对象,就是上面的数据处理函数。
数据处理函数的返回值
因为可能收到的数据不满足一个报文,所以这个函数里多条判断,报文解析不成功都会返回空,调用得到的字符串内容为空时跳出继续读取。解析成功后返回结果将内容发送给客户端
解析报文时有多条判断,先找\n,找到说明有数据的长度,然后根据长度获取内容,检查数据长度和计算的长度符不符合。不满足上面条件的不予处理,否则说明数据长度符合,移除解析了的内容
server.hpp
#pragma once
#include <string>
#include <signal.h>
#include <functional>
#include "log.hpp"
#include "Socket.hpp"using namespace std;
using func_t = std::function<std::string(std::string &package)>;
class server
{
public:server(uint16_t port, func_t fun):_port(port), _fun(fun){}void init(){//创建套接字_listensocket.Socket();_listensocket.Bind(_port);_listensocket.Listen();lg.logmessage(info, "init server done");}void start(){signal(SIGCHLD, SIG_IGN);signal(SIGPIPE, SIG_IGN);int cnt = 1;while (true){string ip;uint16_t port;int sockfd = _listensocket.Accept(&ip, &port);if (sockfd < 0){continue;}lg.logmessage(info, "get a new link %d", sockfd);if (fork() == 0){_listensocket.Close();string inbuff_stream;// 提供服务while (true){char buff[1280];ssize_t n = read(sockfd, buff, sizeof(buff));if (n > 0){buff[n] = 0;lg.logmessage(debug, "\n%s", buff);inbuff_stream += buff;while (true){string echo = _fun(inbuff_stream);if (echo.empty()){break;}lg.logmessage(debug, "缓冲区\n%s", inbuff_stream.c_str());lg.logmessage(debug, "结果\n%s", echo.c_str());cout << "次数:" << cnt++ << endl;write(sockfd, echo.c_str(), echo.size());}}else if (n == 0){break;}else{break;}}exit(0);}close(sockfd);}}~server(){}
private:Sock _listensocket;uint16_t _port;func_t _fun;
};
server.cc
#include <unistd.h>
#include "server.hpp"
#include "servercal.hpp"
//#include "protocol.hpp"int main()
{ServerCal cal;uint16_t port = 8000;server *tsvp = new server(port, std::bind(&ServerCal::Calcluator, &cal, std::placeholders::_1));tsvp->init();daemon(0, 0);tsvp->start();return 0;
}
std::bind绑定函数和第一个参数
9. 客户端
客户端创建连接,成功生成5次随机的数字和运算符,赋值给请求类,封装后发送,收到内容后用结果类解析
client.cc
#include <time.h>
#include <unistd.h>
#include <assert.h>
#include "Socket.hpp"
#include "protocol.hpp"int main()
{srand(time(NULL));uint16_t port = 8000;string ip = "106.54.46.147";struct sockaddr_in server;bzero(&server, sizeof(server));server.sin_family = AF_INET;server.sin_addr.s_addr = inet_addr(ip.c_str());server.sin_port = htons(port);const string opers = "+-*/%";Sock socket;socket.Socket();bool r = socket.Connect(ip, port);if (!r)return 1;int cnt = 1;while (cnt <= 5){cout << "=============第" << cnt << "次测试...." << "============" << endl;string package;int x = rand() % 100;int y = rand() % 100 + 1;char op = opers[rand() % opers.size()];Request req(x, y, op);req.debugprint();req.serialize(&package);package = encode(package);write(socket._sockfd, package.c_str(), package.size());char buff[1024];int n = read(socket._sockfd, buff, sizeof(buff));string inbuff_stream;if (n > 0){buff[n] = 0;inbuff_stream += buff;string content;bool r = decode(inbuff_stream, &content);assert(r);Response resp;r = resp.deserialize(content);assert(r);resp.debugprint();}cout << "=======================================" << endl;sleep(1);cnt++;}
}
日志
#pragma once
#include <stdarg.h>
#include <iostream>
#include <stdio.h>
#include <cstring>
#include <time.h>
#include <cerrno>
#include <sys/types.h>
#include <sys/stat.h>
#include <fcntl.h>
#include <unistd.h>using namespace std;#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 path "log.txt"class Log
{
public:Log(int style = screen){printstyle = style;dir = "log/";}void enable(int method){printstyle = method;}const char *leveltostring(int level){switch (level){case 0:return "info";break;case 1:return "debug";break;case 2:return "warning";break;case 3:return "error";break;case 4:return "fatal";break;default:return "none";break;}}void printlog(int level, const string &logtxt){switch (printstyle){case screen:cout << logtxt;break;case onefile:printonefile(path, logtxt);break;case classfile:printclassfile(level, logtxt);break;}}void logmessage(int level, const char *format, ...){time_t t = time(0);tm *ctime = localtime(&t);char leftbuff[1024];sprintf(leftbuff, "[%s]%d-%d-%d %d:%d:%d:", leveltostring(level), ctime->tm_year + 1900,ctime->tm_mon + 1, ctime->tm_mday, ctime->tm_hour, ctime->tm_min, ctime->tm_sec);char rightbuff[1024];va_list s;va_start(s, format);vsprintf(rightbuff, format, s);va_end(s);char logtext[2048];sprintf(logtext, "%s %s\n", leftbuff, rightbuff);//printf(logtext);printlog(level, logtext);}void printonefile(const string& logname, const string& logtxt){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);}void printclassfile(int level, const string &logtxt){//log.txt.infostring filename = dir + path;filename += ".";filename += leveltostring(level);printonefile(filename, logtxt);}~Log(){};private:int printstyle;string dir; //分类日志,放入目录中
};// int sum(int n, ...)
// {
// int sum = 0;
// va_list s;
// va_start(s, n);// while (n)
// {
// sum = sum + va_arg(s, int);
// n--;
// }// return sum;
// }
10. 结果示例
可以加入守护进程,在初始化服务器后开启守护
11. json序列化
上面的序列化和反序列化功能都是字符串处理,比较麻烦。有一种数据交换格式json,有序列化和反序列化的功能,可以用json代替。用条件编译试试json发送数据
如果没有先安装
sudo yum install -y jsoncpp-devel
序列化
先创建json里的通用变量,Json::Value,以键值对的方式赋值,key和value
再用一个通用变量嵌套一个value类
调用序列化功能生成字符串打印,有两种风格,style内容换行易读
反序列化
创建value变量,创建read对象,调用parse功能解析内容
利用key-value格式提取内容,嵌套类型定义value变量获取,再提取一次
输出结果
修改protocol文件
用#ifdef #else #endif的格式条件编译,json方式写在else的情况里
Request
Json::Value root;root["x"] = _num1;root["y"] = _num2;root["op"] = _op;Json::FastWriter w;*out = w.write(root);return true;
Json::Value root;Json::Reader r;r.parse(in, root);_num1 = root["x"].asInt();_num2 = root["y"].asInt();_op = root["op"].asInt();return true;
Response
Json::Value root;root["res"] = _result;root["code"] = _code;Json::FastWriter w;*out = w.write(root);return true;
Json::Value root;Json::Reader r;r.parse(in, root);_result = root["res"].asInt();_code = root["code"].asInt();return true;
示例
序列化的工具还有protobuf,二进制,更注重效率,可读性不如json,适用于内部使用
12. 添加条件选项
$号可以引入定义的常量,#会注释后面的内容
13. 再看七层模型
会话层的维护是通过server创建子进程提供服务的,接到链接就创建一个会话
表示层就是上面定义的协议,就是结构体字符串等固有的数据格式,网络标准格式就是序列化添加报头这些动作后的数据
应用层就是处理数据的计算器功能
所以表示层和会话层应用层很难在os实现,根据不同的场景有不同的格式和功能,内容也会有文字声音图像等都有可能,无法在os全部实现
如果客户端连上一直不发数据,会占用资源,可以对时间进行判断,超时直接挂掉
相关文章:

49 序列化和反序列化
本章重点 理解应用层的作用,初识http协议 理解传输层的作用,深入理解tcp的各项特性和机制 对整个tcp/ip协议有系统的理解 对tcp/ip协议体系下的其他重要协议和技术有一定的了解 学会使用一些网络问题的工具和方法 目录 1.应用层 2.协议概念 3. 网络计…...

PS —— 精修图像
PS —— 精修图像 修复污点修复画笔工具修复画笔工具 美白滤镜去杂锐化加杂减淡和锐化工具 我觉得今天这篇博客,无论是男同胞还是女同胞,都要熟练掌握(哈哈哈哈…) 今天我们来学习如何精修图像,精修图像一般分为几步——修复&…...

哥白尼哨兵系列卫星数据不能下载的解决方法
自2023年1月24日起,一个新的哥白尼数据空间生态系统已经启动,为所有哨兵数据(Sentinel-1, Sentinel-2, Sentinel-3 and Sentinel-5P)提供可视化和数据处理,地址为:https://dataspace.copernicus.eu/。详细介…...

结构型模式之桥接模式
文章目录 概述原理结构图代码示例 小结 概述 桥接模式(bridge pattern) 的定义是:将抽象部分与它的实现部分分离,使它们都可以独立地变化。 桥接模式用一种巧妙的方式处理多层继承存在的问题,用抽象关联来取代传统的多层继承,将类之间的静态继承关系转…...

数据结构--顺序表
目录 1.顺序表 1.1顺序表的概念及结构 线性表 2、顺序表分类 2.1顺序表和数组的区别 静态顺序表 动态顺序表 3.顺序表的实现 3.1初始化 随后便可对顺序表初始化 3.2插入数据 尾插 头插 在指定位置插入数据 顺序表的查找 头删、尾删及指定位置删除 实现代码&#x…...

【C++项目】实时聊天的在线匹配五子棋对战游戏
目录 项目介绍 开发环境 核心技术 项目前置知识点介绍 Websocketpp 1. WebSocket基本认识 2. WebSocket协议切换原理解析 3. WebSocket报文格式 4. Websocketpp介绍 5. 搭建一个简单WebSocket服务器 JsonCpp 1. Json格式的基本认识 2. JsonCpp介绍 3. 序列化与反序…...

7.2k star的万能视频解析下载插件
今天给大家介绍一个超级厉害的浏览器插件,可以解析各个平台网页视频——猫抓。 项目简介 猫抓(cat-catch) 是一款资源嗅探扩展插件,他能够帮助你筛选列出当前页面的资源。简单来说,当你打开任意一个带有视频的网页&a…...

dmanywhere的docker制作
dmanywhere的docker制作 官网地址: http://www.dmanywhere.cn/ 下载相关执行文件。 Dockerfile的默认命名是“Dockerfile”, 在构建镜像时,如果没有指定Dockerfile文件,Docker通常会寻找名为“Dockerfile”的文件 1.Dockerf…...

Leetcode | 5-21| 每日一题
2769. 找出最大的可达成数字 考点: 暴力 数学式子计算 思维 题解 通过式子推导: 第一想法是二分确定区间在区间内进行查找是否符合条件的, 本题最关键的便是 条件确定 , 第二种方法: 一般是通过数学公式推导的,这种题目我称为数学式编程题 代码 条件判断式 class Solution {…...

vue3添加收藏网站页面
结构与样式 <template><div class"web_view"><ul><li v-for"web in webList" :key"web.title"><a :href"web.src" :title"web.title" target"_blank"><img :src"web.img&…...

吴恩达深度学习笔记:超 参 数 调 试 、 Batch 正 则 化 和 程 序 框 架(Hyperparameter tuning)3.4-3.5
目录 第二门课: 改善深层神经网络:超参数调试、正 则 化 以 及 优 化 (Improving Deep Neural Networks:Hyperparameter tuning, Regularization and Optimization)第三周: 超 参 数 调 试 、 Batch 正 则 化 和 程 序 框 架(Hyperparameter …...

牛客NC362 字典序排列【中等 DFS Java/Go/PHP】
题目 题目链接: https://www.nowcoder.com/practice/de49cf70277048518314fbdcaba9b42c 解题方法 DFS,剪枝Java代码 import java.util.*;public class Solution {/*** 代码中的类名、方法名、参数名已经指定,请勿修改,直接返回…...
PHP获取文件路径getcwd()、__DIR__、__FILE__的区别
getcwd() getcwd() 是一个函数,它返回当前工作目录(CWD)的完整路径。当前工作目录是脚本开始执行时所在的目录,除非在脚本执行过程中通过 chdir() 函数进行了更改。 $cwd getcwd(); echo $cwd; // 输出当前工作目录的完整路径…...

Kafka(十三)监控与告警
目录 Kafka监控与告警1 解决方案1.2 基础知识JMX监控指标代理查看KafkaJMX远程端口 1.3 真实案例Kafka Exporter:PromethusPromethus Alert ManagerGrafana 1.3 实际操作部署监控和告警系统1.2.1 部署Kafka Exporter1.2.2 部署Prometheus1.2.3 部署AlertManger1.2.4 添加告警规…...

SBC3568启动升级,灵活更换动画logo
今天小智将会带着大家体验如何在openharmony sdk内替换开机logo和动态动画。 1. 更换开机logo 开机logo分为uboot阶段【logo.bmp】和kernel阶段【logo_kernel.bmp】的logo两个文件,对图片的要求是:必须为bmp格式,8或者24位深,且…...
v-if 与 v-show(vue3条件渲染)
v-if 是“真正”的条件渲染,因为它会确保在切换过程中条件块内的事件监听器和子组件适当地被销毁和重建。 v-if 也是惰性的:如果在初始渲染时条件为假,则什么也不做——直到条件第一次变为真时,才会开始渲染条件块。 相比之下&a…...

nuxt: generate打包后访问资源404问题
现象 使用Nuxt.js开发的个人页面,部署到nginx服务器中,/_nuxt/*.js、/_nuxt/*.css等静态问题不能访问,提示404错误。 而我们的这些资源文件是存在的。 解决方法 加上此处代码进行上下文配置 baseURL: /nuxt/ 此时在nginx配置 /nuxt 代理 lo…...
【图像超分】论文精读:Residual Non-local Attention Networks for Image Restoration(RNAN)
第一次来请先看这篇文章:【超分辨率(Super-Resolution)】关于【超分辨率重建】专栏的相关说明,包含专栏简介、专栏亮点、适配人群、相关说明、阅读顺序、超分理解、实现流程、研究方向、论文代码数据集汇总等) 文章目录 前言Abstract1 INTRODUCTION2 RELATED WORK3 RESIDU…...

AI大模型:大数据+大算力+强算法
前言:好久不见,甚是想念,我是辣条,我又回来啦,兄弟们,一别两年,还有多少老哥们在呢? 目录 一年半没更文我干啥去了? AI大模型火了 人工智能 大模型的理解 为什么学习…...

同名在线查询系统微信小程序源码下载支持多种流量主,附带系统教程
同名在线查询系统微信小程序源码下载支持多种流量主这是一款支持查询同名的一款微信小程序 该款小程序支持多种查询模式 重名查询,热度查询,概率香查询 源码免费下载地址抄笔记(chaobiji.cn)...
挑战杯推荐项目
“人工智能”创意赛 - 智能艺术创作助手:借助大模型技术,开发能根据用户输入的主题、风格等要求,生成绘画、音乐、文学作品等多种形式艺术创作灵感或初稿的应用,帮助艺术家和创意爱好者激发创意、提高创作效率。 - 个性化梦境…...
椭圆曲线密码学(ECC)
一、ECC算法概述 椭圆曲线密码学(Elliptic Curve Cryptography)是基于椭圆曲线数学理论的公钥密码系统,由Neal Koblitz和Victor Miller在1985年独立提出。相比RSA,ECC在相同安全强度下密钥更短(256位ECC ≈ 3072位RSA…...

盘古信息PCB行业解决方案:以全域场景重构,激活智造新未来
一、破局:PCB行业的时代之问 在数字经济蓬勃发展的浪潮中,PCB(印制电路板)作为 “电子产品之母”,其重要性愈发凸显。随着 5G、人工智能等新兴技术的加速渗透,PCB行业面临着前所未有的挑战与机遇。产品迭代…...
系统设计 --- MongoDB亿级数据查询优化策略
系统设计 --- MongoDB亿级数据查询分表策略 背景Solution --- 分表 背景 使用audit log实现Audi Trail功能 Audit Trail范围: 六个月数据量: 每秒5-7条audi log,共计7千万 – 1亿条数据需要实现全文检索按照时间倒序因为license问题,不能使用ELK只能使用…...
质量体系的重要
质量体系是为确保产品、服务或过程质量满足规定要求,由相互关联的要素构成的有机整体。其核心内容可归纳为以下五个方面: 🏛️ 一、组织架构与职责 质量体系明确组织内各部门、岗位的职责与权限,形成层级清晰的管理网络…...
Device Mapper 机制
Device Mapper 机制详解 Device Mapper(简称 DM)是 Linux 内核中的一套通用块设备映射框架,为 LVM、加密磁盘、RAID 等提供底层支持。本文将详细介绍 Device Mapper 的原理、实现、内核配置、常用工具、操作测试流程,并配以详细的…...

Linux 内存管理实战精讲:核心原理与面试常考点全解析
Linux 内存管理实战精讲:核心原理与面试常考点全解析 Linux 内核内存管理是系统设计中最复杂但也最核心的模块之一。它不仅支撑着虚拟内存机制、物理内存分配、进程隔离与资源复用,还直接决定系统运行的性能与稳定性。无论你是嵌入式开发者、内核调试工…...
scikit-learn机器学习
# 同时添加如下代码, 这样每次环境(kernel)启动的时候只要运行下方代码即可: # Also add the following code, # so that every time the environment (kernel) starts, # just run the following code: import sys sys.path.append(/home/aistudio/external-libraries)机…...
DiscuzX3.5发帖json api
参考文章:PHP实现独立Discuz站外发帖(直连操作数据库)_discuz 发帖api-CSDN博客 简单改造了一下,适配我自己的需求 有一个站点存在多个采集站,我想通过主站拿标题,采集站拿内容 使用到的sql如下 CREATE TABLE pre_forum_post_…...

Linux 下 DMA 内存映射浅析
序 系统 I/O 设备驱动程序通常调用其特定子系统的接口为 DMA 分配内存,但最终会调到 DMA 子系统的dma_alloc_coherent()/dma_alloc_attrs() 等接口。 关于 dma_alloc_coherent 接口详细的代码讲解、调用流程,可以参考这篇文章,我觉得写的非常…...