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)...

2024年5月26日 十二生肖 今日运势
小运播报:2024年5月26日,星期日,农历四月十九 (甲辰年己巳月庚寅日),法定节假日。 红榜生肖:马、猪、狗 需要注意:牛、蛇、猴 喜神方位:西北方 财神方位:…...

Vue 3 组件基础与模板语法详解
title: Vue 3 组件基础与模板语法详解 date: 2024/5/24 16:31:13 updated: 2024/5/24 16:31:13 categories: 前端开发 tags: Vue3特性CompositionAPITeleportSuspenseVue3安装组件基础模板语法 Vue 3 简介 1. Vue 3 的新特性 Vue 3引入了许多新的特性,以提高框…...

ACM实训冲刺第十八天
统计元音 代码 需要注意的是getchar()和gets(s) #include<stdio.h> #include<string.h> int main(){//测试实例个数int n;scanf("%d",&n) ;char s[100];getchar();while(n--){gets(s);int cnta0,cnte0,cnti0,cnto0,cntu0;for(int j0;j<strlen(…...

22AP70/SS927
Hi3519AV200又叫SS927V100和SD3402V100,或者叫22AP70,是一颗面向市场推出的专业超高清智能网络录像机SoC,专门用来替换之前的Hi3519AV100,2023年推出的业界AI-ISP超高性价比芯片!该芯片最高支持四路sensor输入…...

C++实现的代码行数统计器
代码在GitHubMaolinYe/CodeCounter: C20实现的代码统计器,代码量小于100行,可以统计目录下所有代码文件的行数 (github.com) 前段时间到处面试找实习,有技术负责人的负责人问我C写过多少行,5万还是10万,用来评估熟练度…...

C# 结合 JS 暴改腾讯 IM SDK Demo
目录 关于腾讯 IM SDK Demo 范例运行环境 设计思路 服务端生成地址 IM 服务端接收 IM 客户端程序 小结 关于腾讯 IM SDK Demo 腾讯云即时通信 IM SDK 提供了单聊、群聊、关系链、消息漫游、群组管理、资料管理、直播弹幕等功能,并提供完备的 App 接入及管…...

【Web】CISCN 2024初赛 题解(全)
目录 Simple_php easycms easycms_revenge ezjava mossfern sanic Simple_php 用php -r进行php代码执行 因为ban了引号,考虑hex2bin,将数字转为字符串 php -r eval(hex2bin(16进制)); 注意下面这段报错,因为加不了引号,开…...

【C++进阶】AVL树
0.前言 前面我们已经学习过二叉搜索树了,但如果我们是用二叉搜索树来封装map和set等关联式容器是有缺陷的,很可能会退化为单分支的情况,那样效率就极低了,那么有没有方法来弥补二叉搜索树的缺陷呢? 那么AVL树就出现了&…...

云部署最简单python web
最近在玩云主机,考虑将简单的web应用装上去,通过广域网访问一下,代码很简单,所以新手几乎不会碰到什么问题。 from flask import Flaskapp Flask(__name__)app.route(/) def hello_world():return Hello, World!app.route(/gree…...

【Pytorch】【MacOS】14.m1芯片使用mps进行深度模型训练
读者要先自行安装python以及anaconda,并且配置pytorch环境 第一步 测试环境 import torch # 判断macOS的版本是否支持 print(torch.backends.mps.is_available()) # 判断mps是否可用 print(torch.backends.mps.is_built())如果第一个语句为False,说明当前…...