【Linux网络编程】第十弹---打造初级网络计算器:从协议设计到服务实现
✨个人主页: 熬夜学编程的小林
💗系列专栏: 【C语言详解】 【数据结构详解】【C++详解】【Linux系统编程】【Linux网络编程】
目录
1、Protocol.hpp
1.1、Request类
1.1.1、基本结构
1.1.2、构造析构函数
1.1.3、序列化函数
1.1.4、反序列化函数
1.1.5、获取成员变量函数
1.1.6、设置成员变量值函数
1.2、Response类
1.2.1、基本结构
1.2.2、构造析构函数
1.2.3、序列化函数
1.2.4、反序列化函数
1.2.5、打印函数
1.3、Factory类
1.4、添加报头
1.5、解析报头
2、Service.hpp
2.1、构造析构函数
2.2、IOExcute()
2.3、Recv()
3、NetCal.hpp
3.1、构造析构函数
3.2、Calculator()
4、ClientMain.cc
5、完整代码
5.1、Protocol.hpp
5.2、Service.hpp
5.3、ClientMain.cc
5.4、Makefile
上一弹已经写好服务端代码,并讲解了序列化使用到的json库,这弹开始写协议,并完成网络计算器的代码!
1、Protocol.hpp
该文件实现序列化与反序列使用到的类和相关函数(加报头解报头)!
1.1、Request类
该类是向服务器发送请求的类,需要三个成员变量,_x,_y,_oper(运算符号),统一的计算格式是 _x _oper _y,内部主要是序列化(发送)和反序列化(接受)函数!
1.1.1、基本结构
该类有三个成员变量,_x,_y,_oper(运算符号),序列化,反序列化,构造,析构及其他获取成员变量与打印的函数!
class Request
{
public:Request(){}// 序列化 将结构化转成字符串bool Serialize(std::string *out);// 反序列化 将字符串转成结构化bool Deserialize(const std::string &in);void Print();~Request();int X();int Y();char Oper();
private:int _x;int _y;char _oper; // + - * / % // x oper y
};
1.1.2、构造析构函数
为了方便后面的使用,此处实现两个构造函数,一个无参,一个带参函数,析构函数无需处理!
Request()
{}
Request(int x,int y,char oper):_x(x),_y(y),_oper(oper)
{}~Request()
{}
1.1.3、序列化函数
序列化即将结构化转成字符串,并将字符串以输出型参数传出!
// 序列化 将结构化转成字符串
bool Serialize(std::string *out)
{// 1.自己做: "x oper y" (麻烦)// 2.使用现成的库, xml,json(jsoncpp库), protobufJson::Value root;root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::FastWriter writer;std::string s = writer.write(root);*out = s;return true;
}
1.1.4、反序列化函数
反序列化即将字符串转成结构化,参数传入字符串!
// 反序列化 将字符串转成结构化
bool Deserialize(const std::string &in)
{Json::Value root;Json::Reader reader;bool res = reader.parse(in,root);_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;
}
打印函数
void Print()
{std::cout << _x << std::endl;std::cout << _y << std::endl;std::cout << _oper << std::endl;
}
1.1.5、获取成员变量函数
因为成员变量是私有的,外部访问使用成员函数!
int X()
{return _x;
}
int Y()
{return _y;
}
char Oper()
{return _oper;
}
1.1.6、设置成员变量值函数
void SetValue(int x, int y, char oper)
{_x = x;_y = y;_oper = oper;
}
1.2、Response类
该类是向客户端发送结果的类,需要三个成员变量,_result(计算结果),_code(自定义错误码[0: success 1: div error 2: 非法操作]),_desc(错误码描述),内部主要是序列化(发送)和反序列化(接受)函数!
1.2.1、基本结构
该类有三个成员变量,_result(计算结果),_code(自定义错误码),_desc(错误码描述),序列化,反序列化,构造,析构和打印函数!
注意:为了方便类外访问该类成员变量,该成员变量是公有的!
class Response
{
public:Response();// 序列化 将结构化转成字符串bool Serialize(std::string *out);// 反序列化 将字符串转成结构化bool Deserialize(const std::string &in);~Response();
public:int _result;int _code; // 0: success 1: div error 2: 非法操作std::string _desc;
};
1.2.2、构造析构函数
构造函数直接手动初始化(结果和错误码初始化为0,描述默认初始化为success),析构函数无需处理!
Response():_result(0),_code(0),_desc("success")
{}~Response()
{}
1.2.3、序列化函数
序列化即将结构化转成字符串,并将字符串以输出型参数传出!
// 序列化 将结构化转成字符串
bool Serialize(std::string *out)
{// 使用现成的库, xml,json(jsoncpp库), protobufJson::Value root;root["result"] = _result;root["code"] = _code;root["desc"] = _desc;Json::FastWriter writer;std::string s = writer.write(root);*out = s;return true;
}
1.2.4、反序列化函数
反序列化即将字符串转成结构化,参数传入字符串!
// 反序列化 将字符串转成结构化
bool Deserialize(const std::string &in)
{Json::Value root;Json::Reader reader;bool res = reader.parse(in,root);_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;
}
1.2.5、打印函数
将成员变量以字符串形式打印出来即可!
void PrintResult()
{std::cout << "result: " << _result << ", code: " << _code << ", desc: " << _desc << std::endl;
}
1.3、Factory类
因为Request类和Response类可能频繁创建,因此我们可以设计一个工厂类,内部设计两个创建类的静态函数(没有this指针,外部直接调用函数即可)!
class Factory
{
public:static std::shared_ptr<Request> BuildRequestDefault(){return std::make_shared<Request>();}static std::shared_ptr<Response> BuildResponseDefault(){return std::make_shared<Response>();}
};
1.4、添加报头
在实际的网络通信中,传的不仅仅是序列化之后的字符串,还会有报头信息,此处我们也设计一下报头信息,格式如下:
1、"len"\r\n"{json}"\r\n --- 完整的报文
2、len 有效载荷的长度
3、\r\n(第一个): 区分len 和 json 串
4、\r\n(第二个): 暂时没有其他用,打印方便,debug
static const std::string sep = "\r\n"; // 分隔符// 添加报头
std::string Encode(const std::string &jsonstr)
{int len = jsonstr.size();std::string lenstr = std::to_string(len);return lenstr + sep + jsonstr + sep;
}
1.5、解析报头
将发送过来的有报头的信息解析成有效信息,即去掉前面的长度和分割符与有效信息后面的分隔符!
注意:可能没有一个有效信息或者有多个有效信息!
static const std::string sep = "\r\n"; // 分隔符// 不能带const
// "le
// "len"\r\n"{j [)
// "len"\r\n"{json}"\r\n"
// "len"\r\n"{json}"\r\n"len"\r\n
// "len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"
// "len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"std::string Decode(std::string &packagestream)
{// 分析auto pos = packagestream.find(sep); // 报文流if (pos == std::string::npos)return std::string(); // 没找到返回空std::string lenstr = packagestream.substr(0, pos);int len = std::stoi(lenstr); // json长度// 计算一个完整的报文应该是多长int total = lenstr.size() + len + 2 * sep.size();// 传进来的字符串长度小于报文总长,说明没有一个完整的有效信息,返回空if (packagestream.size() < total)return std::string();// 提取std::string jsonstr = packagestream.substr(pos + sep.size(), len);packagestream.erase(0, total); // 从0位置删除total长度return jsonstr;
}
2、Service.hpp
Service.hpp中的 IOService类 是用于通信的类,而且内部需要执行传入的回调函数,因此该类需要加一个执行方法的成员!
执行方法的声明:
参数是请求类的指针,返回值是应答类的指针!
using process_t = std::function<std::shared_ptr<Response>(std::shared_ptr<Request>)>;
2.1、构造析构函数
构造函数需要传入函数对象,用于初始化成员变量,析构函数无需处理!
IOService(process_t process) :_process(process)
{}~IOService()
{}
2.2、IOExcute()
IOExcute()函数进行客户端与服务端的通信,并处理发送过来的信息(调用执行方法),有以下7个主要步骤!
1、接收消息
2、报文解析(保证获取至少获得一条有效信息,没有则继续接受消息)
3、反序列化(将字符串转成结构化)
4、业务处理(调用构造函数传入的回调函数)
5、序列化应答
6、添加len长度(报头)
7、发送回去
void IOExcute(SockSPtr sock, InetAddr &addr)
{std::string packagestreamqueue; // 写在while循环外,存储信息while (true){// 1.负责读取ssize_t n = sock->Recv(&packagestreamqueue);if(n <= 0){LOG(INFO, "client %s quit or recv error\n", addr.AddrStr().c_str());break;}std::cout << "--------------------------------------------" << std::endl;std::cout << "packagestreamqueue: \n" << packagestreamqueue << std::endl;// 我们能保证读到的是完整的报文? 不能!// 2.报文解析,提取报头和有效载荷std::string package = Decode(packagestreamqueue);if(package.empty()) continue;// 我们能保证读到的是一个完整的报文!!!auto req = Factory::BuildRequestDefault();std::cout << "package: \n" << package << std::endl;// 3.反序列化req->Deserialize(package); // 反序列化 将字符串转成结构化// 4.业务处理auto resp = _process(req); // 业务处理(通过请求,得到应答)// 5.序列化应答std::string respjson;resp->Serialize(&respjson); // 序列化std::cout << "respjson: \n" << respjson << std::endl;// 6.添加len长度respjson = Encode(respjson);std::cout << "respjson add header done: \n" << respjson << std::endl;// 7.发送回去sock->Send(respjson);}
此处有一个问题,如果第一次接收消息没有读到完整的报文就会继续接受消息,但是以我们前面写的接收消息函数会清空内容,因此我们需要做稍微的修改!
2.3、Recv()
Recv()函数是Socket.hpp文件中TcpServer类的成员函数,接收消息成功之后需要该为拼接旧的内容!
// 接收消息
ssize_t Recv(std::string *out) override
{char inbuffer[4096];ssize_t n = ::recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if (n > 0){inbuffer[n] = 0;// *out = inbuffer;*out += inbuffer; // 调整(可能一次读取不成功 | 读取多次)}return n;
}
3、NetCal.hpp
NetCal.hpp文件中的NetCal类包含回调函数的具体实现!
3.1、构造析构函数
该类没有成员变量,构造析构函数无需处理!
NetCal()
{}~NetCal()
{}
3.2、Calculator()
Calculator() 函数用于网络计算器的计算逻辑!
std::shared_ptr<Response> Calculator(std::shared_ptr<Request> req)
{auto resp = Factory::BuildResponseDefault();switch (req->Oper()){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->_code = 1;resp->_desc = "divc zero";}else{resp->_result = req->X() / req->Y();}}break;case '%':{if(req->Y() == 0){resp->_code = 2;resp->_desc = "mod zero";}else{resp->_result = req->X() % req->Y();}}break;default:{resp->_code = 3;resp->_desc = "illegal operation";}break;}return resp;
}
4、ClientMain.cc
该文件用户创建TcpServer类对象,并调用执行函数运行客户端!
通信操作主要包括以下七步:
1、序列化
2、添加长度报头字段
3、发送数据
4、读取应答,response
5、报文解析,提取报头和有效载荷
6、反序列化
7、打印结果
#include <iostream>
#include <ctime>
#include "Socket.hpp"
#include "Protocol.hpp"using namespace socket_ns;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);SockSPtr sock = std::make_shared<TcpSocket>();if (!sock->BuildClientSocket(serverip, serverport)){std::cerr << "connect error" << std::endl;exit(1);}srand(time(nullptr) ^ getpid());const std::string opers = "+-*/%&^!";std::string packagestreamqueue;while (true){// 构建数据int x = rand() % 10;usleep(x * 1000);int y = rand() % 10;usleep(x * y * 100);char oper = opers[y % opers.size()];// 构建请求auto req = Factory::BuildRequestDefault();req->SetValue(x, y, oper);// 1.序列化std::string reqstr;req->Serialize(&reqstr);// 2.添加长度报头字段reqstr = Encode(reqstr);std::cout << "####################################" << std::endl;std::cout << "requset string: \n" << reqstr << std::endl;// 3.发送数据sock->Send(reqstr);while (true){// 4.读取应答,responsessize_t n = sock->Recv(&packagestreamqueue);if (n <= 0){break;}// 我们能保证读到的是完整的报文? 不能!// 5.报文解析,提取报头和有效载荷std::string package = Decode(packagestreamqueue);if (package.empty())continue;std::cout << "package: \n" << package << std::endl;// 6.反序列化auto resp = Factory::BuildResponseDefault();resp->Deserialize(package);// 7.打印结果resp->PrintResult();break;}sleep(1);}sock->Close();return 0;
}
5、完整代码
5.1、Protocol.hpp
#pragma once
#include <iostream>
#include <memory>
#include <string>
#include <jsoncpp/json/json.h>static const std::string sep = "\r\n"; // 分隔符// 设计一下协议的报头和报文的完整格式
// "len"\r\n"{json}"\r\n --- 完整的报文
// len 有效载荷的长度
// \r\n(第一个): 区分len 和 json 串
// \r\n(第二个): 暂时没有其他用,打印方便,debug// 添加报头
std::string Encode(const std::string &jsonstr)
{int len = jsonstr.size();std::string lenstr = std::to_string(len);return lenstr + sep + jsonstr + sep;
}// 不能带const
// "le
// "len"\r\n"{j [)
// "len"\r\n"{json}"\r\n"
// "len"\r\n"{json}"\r\n"len"\r\n
// "len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"
// "len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"len"\r\n"{json}"\r\n"std::string Decode(std::string &packagestream)
{// 分析auto pos = packagestream.find(sep); // 报文流if (pos == std::string::npos)return std::string(); // 没找到返回空std::string lenstr = packagestream.substr(0, pos);int len = std::stoi(lenstr); // json长度// 计算一个完整的报文应该是多长int total = lenstr.size() + len + 2 * sep.size();// 传进来的字符串长度小于报文总长,说明没有一个完整的有效信息,返回空if (packagestream.size() < total)return std::string();// 提取std::string jsonstr = packagestream.substr(pos + sep.size(), len);packagestream.erase(0, total); // 从0位置删除total长度return jsonstr;
}// 协议
class Request
{
public:Request(){}Request(int x, int y, char oper) : _x(x), _y(y), _oper(oper){}// 序列化 将结构化转成字符串bool Serialize(std::string *out){// 1.自己做: "x oper y" (麻烦)// 2.使用现成的库, xml,json(jsoncpp库), protobufJson::Value root;root["x"] = _x;root["y"] = _y;root["oper"] = _oper;Json::FastWriter writer;std::string s = writer.write(root);*out = s;return true;}// 反序列化 将字符串转成结构化bool Deserialize(const std::string &in){Json::Value root;Json::Reader reader;bool res = reader.parse(in, root);_x = root["x"].asInt();_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}void Print(){std::cout << _x << std::endl;std::cout << _y << std::endl;std::cout << _oper << std::endl;}~Request(){}int X(){return _x;}int Y(){return _y;}char Oper(){return _oper;}void SetValue(int x, int y, char oper){_x = x;_y = y;_oper = oper;}private:int _x;int _y;char _oper; // + - * / % // x oper y
};// class request resp = {30,0}
class Response
{
public:Response() : _result(0), _code(0), _desc("success"){}// 序列化 将结构化转成字符串bool Serialize(std::string *out){// 使用现成的库, xml,json(jsoncpp库), protobufJson::Value root;root["result"] = _result;root["code"] = _code;root["desc"] = _desc;Json::FastWriter writer;std::string s = writer.write(root);*out = s;return true;}// 反序列化 将字符串转成结构化bool Deserialize(const std::string &in){Json::Value root;Json::Reader reader;bool res = reader.parse(in, root);if (!res)return false;_result = root["result"].asInt();_code = root["code"].asInt();_desc = root["desc"].asString();return true;}void PrintResult(){std::cout << "result: " << _result << ", code: " << _code << ", desc: " << _desc << std::endl;}~Response(){}public:int _result;int _code; // 0: success 1: div error 2: 非法操作std::string _desc;
};class Factory
{
public:static std::shared_ptr<Request> BuildRequestDefault(){return std::make_shared<Request>();}static std::shared_ptr<Response> BuildResponseDefault(){return std::make_shared<Response>();}
};
5.2、Service.hpp
#pragma once
#include <iostream>
#include <functional>
#include "InetAddr.hpp"
#include "Socket.hpp"
#include "Log.hpp"
#include "Protocol.hpp"using namespace socket_ns;
using namespace log_ns;using process_t = std::function<std::shared_ptr<Response>(std::shared_ptr<Request>)>;class IOService
{
public:IOService(process_t process) :_process(process){}void IOExcute(SockSPtr sock, InetAddr &addr){std::string packagestreamqueue; // 写在while循环外,存储信息while (true){// 1.负责读取ssize_t n = sock->Recv(&packagestreamqueue);if(n <= 0){LOG(INFO, "client %s quit or recv error\n", addr.AddrStr().c_str());break;}std::cout << "--------------------------------------------" << std::endl;std::cout << "packagestreamqueue: \n" << packagestreamqueue << std::endl;// 我们能保证读到的是完整的报文? 不能!// 2.报文解析,提取报头和有效载荷std::string package = Decode(packagestreamqueue);if(package.empty()) continue;// 我们能保证读到的是一个完整的报文!!!auto req = Factory::BuildRequestDefault();std::cout << "package: \n" << package << std::endl;// 3.反序列化req->Deserialize(package); // 反序列化 将字符串转成结构化// 4.业务处理auto resp = _process(req); // 业务处理(通过请求,得到应答)// 5.序列化应答std::string respjson;resp->Serialize(&respjson); // 序列化std::cout << "respjson: \n" << respjson << std::endl;// 6.添加len长度respjson = Encode(respjson);std::cout << "respjson add header done: \n" << respjson << std::endl;// 7.发送回去sock->Send(respjson);}}// 测试// void IOExcute(SockSPtr sock, InetAddr &addr)// {// while (true)// {// std::string message;// ssize_t n = sock->Recv(&message);// if(n > 0)// {// LOG(INFO, "get message from client [%s],message: %s\n", addr.AddrStr().c_str(), message.c_str());// std::string hello = "hello";// sock->Send(hello);// }// else if(n == 0)// {// LOG(INFO, "client %s quit\n", addr.AddrStr().c_str());// break;// }// else // {// LOG(ERROR, "read error\n", addr.AddrStr().c_str());// break;// }// }// }~IOService(){}
private:process_t _process;
};
5.3、ClientMain.cc
#include <iostream>
#include <ctime>
#include "Socket.hpp"
#include "Protocol.hpp"using namespace socket_ns;int main(int argc, char *argv[])
{if (argc != 3){std::cerr << "Usage: " << argv[0] << " server-ip server-port" << std::endl;exit(0);}std::string serverip = argv[1];uint16_t serverport = std::stoi(argv[2]);SockSPtr sock = std::make_shared<TcpSocket>();if (!sock->BuildClientSocket(serverip, serverport)){std::cerr << "connect error" << std::endl;exit(1);}srand(time(nullptr) ^ getpid());const std::string opers = "+-*/%&^!";std::string packagestreamqueue;while (true){// 构建数据int x = rand() % 10;usleep(x * 1000);int y = rand() % 10;usleep(x * y * 100);char oper = opers[y % opers.size()];// 构建请求auto req = Factory::BuildRequestDefault();req->SetValue(x, y, oper);// 1.序列化std::string reqstr;req->Serialize(&reqstr);// 2.添加长度报头字段reqstr = Encode(reqstr);std::cout << "####################################" << std::endl;std::cout << "requset string: \n" << reqstr << std::endl;// 3.发送数据sock->Send(reqstr);while (true){// 4.读取应答,responsessize_t n = sock->Recv(&packagestreamqueue);if (n <= 0){break;}// 我们能保证读到的是完整的报文? 不能!// 5.报文解析,提取报头和有效载荷std::string package = Decode(packagestreamqueue);if (package.empty())continue;std::cout << "package: \n" << package << std::endl;// 6.反序列化auto resp = Factory::BuildResponseDefault();resp->Deserialize(package);// 7.打印结果resp->PrintResult();break;}sleep(1);}sock->Close();return 0;
}
5.4、Makefile
.PHONY:all
all:calserver calclientcalserver:ServerMain.cc g++ -o $@ $^ -std=c++14 -ljsoncppcalclient:ClientMain.cc g++ -o $@ $^ -std=c++14 -ljsoncpp.PHONY:clean
clean:rm -rf calserver calclient
相关文章:
【Linux网络编程】第十弹---打造初级网络计算器:从协议设计到服务实现
✨个人主页: 熬夜学编程的小林 💗系列专栏: 【C语言详解】 【数据结构详解】【C详解】【Linux系统编程】【Linux网络编程】 目录 1、Protocol.hpp 1.1、Request类 1.1.1、基本结构 1.1.2、构造析构函数 1.1.3、序列化函数 1.1.4、反…...
嵌入式 linux Git常用命令 抽补丁 打补丁
Git常用命令 为什么要学习git呢?我相信刚入门的小伙伴敲打肯定碰到过这种玄学问题,我明明刚刚还能用的代码,后面不知道咋的就不能用了,所以每次你调出一个功能点以后都会手动复制一份代码防止出问题,时间一长发现整个…...
windows C#-方法概述(上)
方法是包含一系列语句的代码块。 程序通过调用该方法并指定任何所需的方法参数使语句得以执行。 在 C# 中,每个执行的指令均在方法的上下文中执行。 Main 方法是每个 C# 应用程序的入口点,并在启动程序时由公共语言运行时 (CLR) 调用。 在使用顶级语句的…...
SpringCloud和Nacos的基础知识和使用
1.什么是SpringCloud 什么是微服务? 假如我们需要搭建一个网上购物系统,那么我们需要哪些功能呢?商品中心、订单中心和客户中心等。 当业务功能较少时,我们可以把这些功能塞到一个SpringBoot项目中来进行管理。但是随…...
一行一行出字的视频怎么做?简单的操作方法
在视频制作中,逐行出现的字幕效果不仅能够增强视觉冲击力,还能让观众更加专注于内容,特别适合用于教育视频、书单推荐、诗歌朗诵等多种场景。下面,我们将详细介绍如何影忆,来制作这种逐行出字的视频效果。 1.字幕逐行…...
鸿蒙NEXT开发案例:颜文字搜索器
【引言】 本文将介绍一个名为“颜文字搜索器”的开发案例,该应用是基于鸿蒙NEXT平台构建的,旨在帮助用户快速查找和使用各种风格的表情符号。通过本案例的学习,读者可以了解如何在鸿蒙平台上进行数据处理、UI设计以及交互逻辑的实现。 【环…...
python在纯文本程序里面藏一张图
思路base64编码是纯文本的,base64是以字符串的形式存在,包括但不限于python,js,C#,C/Cpp. 这里给出python示例:运行后可以显示一张opencv的官方例程图. 废话不说,上程序. import os,sys,time,cv2,base64,requests from PIL import Image from io import BytesIO import nump…...
Java 身份证校验工具类(15位校验、18位校验与15转18)
文章目录 身份证简介(一)身份证号码的组成(二)一代和二代身份证一代身份证二代身份证 检验思路分析(一)15位身份证号码(二)18位身份证号码(三)校验算法示例&a…...
The Past, Present and Future of Apache Flink
摘要:本文整理自阿里云开源大数据负责人王峰(莫问)在 Flink Forward Asia 2024上海站主论坛开场的分享,今年正值Flink开源项目诞生的第10周年,借此时机,王峰回顾了Flink在过去10年的发展历程以及 Flink社区…...
不能通过 ip 直接访问 共享盘 解决方法
from base_config.config import OpenSMB, SMB import os, time, calendar, requests, decimal, platform, fs.smbfsinfo_dict SMB.EPDI_dict info_dict[host] (FS03,10.6.12.182) info_dict[direct_tcp] True# smb OpenSMB(info_dict)print(ok)# 根据 ip 查询电脑名 impor…...
IDEA方法注释模板设置
目录 创建模板 新建模板:命名为* 设置模板内容-IDEA格式模板 设置模板应用场景 设置参数 创建模板 /**Enter这里我们也按照这种习惯来设置IDEA的方法注释:File-->Settings-->Editor-->Live Templates 先新建模板组,然后在模板组中…...
组件缓存keep-alive
希望点击面经详情回来之后该1面经详情停留在滚动条停止的位置 有些 组件是不需要缓存的,例如详情页不需要缓存。解决方法是keep-alive的三个属性 include:组件名数组,只有匹配的组件会被缓存exclude:组件名数组,任何匹配的组件都不会被缓存ma…...
【经验分享】搭建本地训练环境知识点及方法
最近忙于备考没关注,有次点进某小黄鱼发现首页出现了我的笔记还被人收费了 虽然我也卖了一些资源,但我以交流、交换为主,笔记都是免费给别人看的 由于当时刚刚接触写的并不成熟,为了避免更多人花没必要的钱,所以决定公…...
Ant Design of Vue之带select控件,单元格编辑功能的表格EditableCell组件
效果图 功能 表格里面某一行或者某一个单元格支持select复选框可以编辑,新增一行数据,删除一行数据,并且有校验规则 源码 editablecell组件源码 参考自 源码...
etcd节点扩/缩容
etcd集群节点数越多越好吗? etcd 集群是一个 Raft Group,没有 shared。所以它的极限有两部分,一是单机的容量限制,内存和磁盘;二是网络开销,每次 Raft 操作需要所有节点参与,每一次写操作需要集…...
FFmpeg功能使用
步骤:1,安装FFmpeg Download FFmpeg 在这里点击->Windows builds from gyan.dev;如下图 会跳到另外的下载界面: 在里面下拉选择点击ffmpeg-7.1-essentials_build.zip: 即可下载到FFmpeg; 使用&#…...
动手学深度学习-线性神经网络-7softmax回归的简洁实现
目录 初始化模型参数 重新审视Softmax的实现 优化算法 训练 小结 在 线性回归的实现中, 我们发现通过深度学习框架的高级API能够使实现 线性回归变得更加容易。 同样,通过深度学习框架的高级API也能更方便地实现softmax回归模型。 本节如在上一节…...
GenAI + 3D:开启4D场景重建的新纪元
近日,一项激动人心的研究成果在《GenAI + 3D》方向取得了重大进展,它提出了一种创新的方法,能够将普通的2D视频转换为完整的4D场景。想象一下,《黑客帝国》中的"子弹时间"效果——现在你不仅可以停留在一个特定的时刻来改变视角,还可以自由地在空间和时间中移动…...
记一个framebuffer显示混乱的低级错误
记一个framebuffer显示混乱的低级错误 由于framebuffer的基础知识不扎实,这个任务上我多卡了两天,差点把我搞死,于此记录为后鉴。 打算用awtk做一个多进程项目,计划把framebuffer的内容通过websocket输出到浏览器上去显示画面, …...
网络安全教学博客(二):常见网络安全威胁剖析
在上一篇博客中,我们了解了网络安全的基础概念和重要性。今天,让我们深入探讨一下常见的网络安全威胁,以便我们能够更好地识别和防范它们。 恶意软件(Malware) 病毒(Virus):病毒是一…...
【ZYNQ开发】Vitis下保存与快速加载BSP配置的方法
在使用Xilinx的Vitis进行ZYNQ PS端开发时,常常涉及到对于BSP的设置,比较典型的像是使用lwip时,需要对DHCP、memory以及send_buffer等进行一些自定义的修改。在设计到硬件描述文件需要进行更换(PL端更改程序)重新加载工…...
go开发中interface和方法接收器的使用
Go 语言中的接口和方法接收器学习 Go 中的 interface 就像是一个神奇的魔法杖,能让你轻松地将不同的类型拉到同一个阵营里。与其他语言的接口不同,Go 的接口无需显式声明“我实现了你”,只要你满足了接口规定的方法,Go 就会自动认…...
【昇腾】NPU ID:物理ID、逻辑ID、芯片映射关系
起因: https://www.hiascend.com/document/detail/zh/Atlas%20200I%20A2/23.0.0/re/npu/npusmi_013.html npu-smi info -l查询所有NPU设备: [naienotebook-npu-bd130045-55bbffd786-lr6t8 DCNN]$ npu-smi info -lTotal Count : 1NPU…...
景联文科技提供高质量文本标注服务,驱动AI技术发展
文本标注是指在原始文本数据上添加标签的过程,这些标签可以用来指示特定的实体、关系、事件等信息,以帮助计算机理解和处理这些数据。 文本标注是自然语言处理(NLP)领域的一个重要环节,它通过为文本的不同部分提供具体…...
【Spark】Spark Join类型及Join实现方式
如果觉得这篇文章对您有帮助,别忘了点赞、分享或关注哦!您的一点小小支持,不仅能帮助更多人找到有价值的内容,还能鼓励我持续分享更多精彩的技术文章。感谢您的支持,让我们一起在技术的世界中不断进步! Sp…...
docker安装、升级、以及sudo dockerd --debug查看启动失败的问题
1、docker安装包tar下载地址 Index of linux/static/stable/x86_64/ 2、下载tgz文件并解压 tar -zxvf docker-24.0.8.tgz 解压后docker文件夹下位docker相关文件 3、将老版本docker相关文件,备份 将 /usr/bin/docker下docker相关的文件,mv到备份目录…...
做T和做T+0有什么区别
做T和做T0在股市中实际上有紧密的联系,但也有所区别。以下是对两者的详细比较: 一、定义与原理 做T: 广义上,做T指的是一种通过低买高卖或高卖低买来赚取差价的交易策略。这种策略可以应用于不同的交易周期,包括日内交…...
【JAVA项目】基于ssm的【美食推荐管理系统】
【JAVA项目】基于ssm的【美食推荐管理系统】 技术简介:采用JSP技术、B/S架构、SSM框架、MySQL技术等实现。 系统简介:美食推荐管理系统,在系统首页可以查看首页、热门美食、美食教程、美食店铺、美食社区、美食资讯、我的、跳转到后台等内容。…...
从零开始学docker(五)-可用的docker镜像
最近docker镜像都不能访问,目前亲测可用的docker镜像可用,并拉取mysql测试完成。 [缺点] docker search 查不到镜像的索引列表,只能手动查询索引目录(解决方案在最后)。 linux服务器vim打开镜像文件daemon.json vim /e…...
repmgr集群部署-PostgreSQL高可用保证
📢📢📢📣📣📣 作者:IT邦德 中国DBA联盟(ACDU)成员,10余年DBA工作经验, Oracle、PostgreSQL ACE CSDN博客专家及B站知名UP主,全网粉丝10万 擅长主流Oracle、My…...
2020数字中国创新大赛-虎符网络安全赛道丨Web Writeup
本文是i春秋论坛作家「OPLV1H」表哥参加2020数字中国创新大赛-虎符网络安全赛道线上初赛的赛后总结,关于Web的Writeup记录,感兴趣的小伙伴快来学习吧。 1、hash_file — 是使用给定文件的内容生成哈希值,和文件名称无关。 2、jwt令牌结构和j…...
手动部署前后端项目到LInux上面
一:部署后端工程 在本地的idea中先启动当前的工程,然后访问一下,看看工程是否正常访问。 执行package指令,进行打包操作,将当前的springboot项目,打成一个jar包。 <!-- SpringBoot应用打包插件--> …...
【一本通】intervals
【一本通】intervals 💐The Begin💐点点关注,收藏不迷路💐 给出n个闭区间[ai,bi]和n个整数c1,……,cn。令Z表示一个整数集合,Z集合中最少要包含多少个整数可以使得每个区[ai,bi]都至少有ci个整数位于Z集合中。 输入 …...
测试脚本并发多进程:pytest-xdist用法
参考:https://www.cnblogs.com/poloyy/p/12694861.html pytest-xdist详解: https://www.cnblogs.com/poloyy/p/14708825.html 总 https://www.cnblogs.com/poloyy/category/1690628.html...
ALOHA 协议详解
注:本文为 “ALOHA 协议” 相关文章合辑。 未去重整理。 动态分配信道(ALOHA 协议、CSMA 协议) QuantumYou 于 2021-07-27 09:32:04 发布 ALOHA 协议 纯 ALOHA 协议 -纯 ALOHA 协议思想:不监听信道,不按时间槽发送…...
ios h5中在fixed元素中的input被focus时,键盘遮挡input (van-popup、van-feild)
问题描述: 前提:我使用的是vant组件库,其中一个页面中有一个van-popup组件,van-popup组件中又嵌套了一个van-field组件预期结果:当点击van-feild输入框时,键盘弹起,输入框显示在键盘上方实际结…...
【Mysql】索引下推、索引合并详解
文章目录 1. 索引下推(Index Condition Pushdown, ICP)定义工作机制实现过程优化的典型场景 2. 索引合并(Index Merge)定义索引合并方式使用限制 3. 对比与应用场景选用建议 这篇文章就简单的给大家介绍下索引下推、索引合并 1. 索…...
简易记事本项目—基于SSM+Vue前后端分离
🌈🌈🌈今天给大家分享的是:基于SSMVue的简易记事本项目 目录 引言 技术栈介绍 项目概述 1. 用户注册 2. 用户登录 3. 用户退出 4. 事件分类 5. 事件管理 项目主要图片 引言 在快节奏的现代生活中,我们常常被…...
Java转C之C/C++ 的调试和内存分析
C/C 的调试和内存分析工具非常丰富,这些工具可以帮助开发者定位错误、分析程序行为,以及检测内存问题(如内存泄漏、非法访问等)。下面将详细介绍常见的调试器和内存分析工具,并进行分类讲解。 一、调试器 1. GDB (GNU…...
Python 面向对象编程全面解析与深度探索
目录 类和对象的概念 类(Class) 对象(Object) (一)属性(Attributes) (a).实例属性(Instance Attributes) (b).类属性(Class Att…...
零配置打包工具 Parcel 的详细使用指南
前言 在前端开发中,选择一个高效且易用的打包工具至关重要。Parcel 作为一款零配置的 Web 应用打包工具,凭借其卓越的性能和简单的使用体验,赢得了众多开发者的青睐。它不仅能够自动处理依赖关系和代码打包,还支持热模块替换和多…...
批量查找文件关键字-工具
string find...
freeswitch(开启支持MCU视频会议,使用mod_av模块)
亲测版本centos 7.9系统–》 freeswitch1.10.9 本人freeswitch安装路径(根据自己的路径进入) /usr/local/freeswitch/etc/freeswitch场景说明: 有些场景想使用视频会议MCU融合画面进行开会使用方法: 第一步:下载插件 yum install -y epel-release yum install...
Quant connect的优势和不足,学习曲线难
Quant connect的优势和不足 Quant connect作为一个成熟的算法交易平台,具有许多优势,包括: 强大的回测功能:Quant connect提供了丰富的数据源和回测功能,可以对各种交易策略进行全面的回测和分析。 容易上手…...
对rust的全局变量使用drop方法
文章目录 rust处理全局变量的策略方法1:在main中自动Drop全局变量 参考 rust处理全局变量的策略 Rust 的静态变量不会在程序退出时自动调用 Drop,因为它们的生命周期与进程绑定。 use std::sync::OnceLock;struct GlobalData {content: String, }impl …...
使用FastGPT制做一个AI网站日志分析器
越来越的多网站面临每天上千次的扫描和各类攻击,及时发现攻击IP,并有效的屏蔽不良访问成为网站安全的重要保障,这里我们使用AI来完成对网站日志的日常分析。 我们来使用FastGPT来制做一个AI网站日志析器,下面就开始: …...
无限次使用 cursor pro
github地址 cursor-vip 使用方式 在 MacOS/Linux 中,请打开终端; 在 Windows 中,请打开 Git Bash。 然后执行以下命令来安装: 部分电脑可能会误报毒,需要关闭杀毒软件/电脑管家/安全防护再进行 方式1:通过…...
vuex 作用及五大组成部分
Vuex 是 Vue.js 的官方状态管理库,旨在帮助开发者构建大型应用时更好地管理和共享全局状态。它提供了一种集中式存储和管理应用所有组件的状态的方式,并且遵循单一状态树的原则。通过 Vuex,可以更容易地实现状态的可预测性和调试。 一、Vuex…...
Centos7上Jenkins+Docker+Git+SpringBoot自动化部署
文章目录 1.宿主机安装maven2.安装jenkins3.配置Jenkins4.Jenkins脚本自动安装JDK(可选) 1.宿主机安装maven wget https://dlcdn.apache.org/maven/maven-3/3.9.9/binaries/apache-maven-3.9.9-bin.tar.gz mv apache-maven-3.9.9-bin.tar.gz /usr/local…...
MATLAB图卷积神经网络GCN处理分子数据集节点分类研究
全文链接:https://tecdat.cn/?p38570 本文主要探讨了如何利用图卷积网络(GCN)对图中的节点进行分类。介绍了相关的数据处理、模型构建、训练及测试等环节,通过对分子数据集的操作实践,展示了完整的节点分类流程&#…...