【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、反…...

无限弹窗?无限重启?
Windows开机自启目录: "%USERPROFILE%\AppData\Roaming\Microsoft\windows\StartMenu\Programs\Startup" 基于这个和 start 命令, shutdown 命令, 编写 bat 病毒程序。 无限弹窗 echo start cmd > hack.txt echo %0 >>…...

深入详解人工智能机器学习常见算法中的K-means聚类
目录 引言 1. K-means聚类的基本概念 1.1 K-means聚类的定义 1.2 K-means聚类的核心思想 1.3 K-means聚类的目标函数 2. K-means聚类的核心原理 2.1 初始化 2.2 分配 2.3 更新 2.4 迭代 3. K-means聚类的具体实现 3.1 K-means聚类的算法流程 3.2 K-means聚类的Pyt…...

lc146LRU缓存——模仿LinkedHashMap
146. LRU 缓存 - 力扣(LeetCode) 法1: 调用java现有的LinkedHashMap的方法,但不太理解反正都不需要扩容,super(capacity, 1F, true);不行吗,干嘛还弄个装载因子0.75还中途扩容一次浪费时间。 class LRUC…...

全面深入解析:C语言动态库
引言 动态库(Dynamic Library)是现代软件开发中不可或缺的一部分,它们不仅提高了代码的重用性和维护性,还显著提升了系统的性能和资源利用率。本文将全面探讨C语言中的动态库,从基础概念到高级应用,通过丰…...

运用 SSM 实现垃圾分类系统智能化升级
目 录 摘 要 1 前 言 3 第1章 概述 4 1.1 研究背景 4 1.2 研究目的 4 1.3 研究内容 4 第二章 开发技术介绍 5 2.1Java技术 6 2.2 Mysql数据库 6 2.3 B/S结构 7 2.4 SSM框架 8 第三章 系统分析 9 3.1 可行性分析 9 3.1.1 技术可行性 9 3.1.2 经济可行性 10 3.1.3 操作可行性 10 …...

LeNet-5:深度学习与卷积神经网络的里程碑
目录 编辑 引言 LeNet-5的结构与原理 输入层 C1层:卷积层 S2层:池化层 C3层:卷积层 S4层:池化层 C5层:卷积层 F6层:全连接层 输出层 LeNet-5的算法基础 LeNet-5的优点 LeNet-5的现代应用 …...

从资产流动分析WIF市场潜力X.game深究其他未知因素
近日,两则关于WIF最新消息引起了投资者们的注意。据报道,11月28日Vintermute在过去13小时内累计从Binance交易所提取了价值533万美元的WIF,此举不仅彰显了其强大的资金实力,更在某种程度上推动了WIF币价的反弹;另一方面…...

深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理
深入解析Vue3响应式系统:从Proxy实现到依赖收集的核心原理 响应式系统的基本原理 作为一个热门的JavaScript框架,Vue在3.x版本中引入了基于Proxy的响应式系统。这个系统的核心思想是利用Proxy对象拦截对数据的访问和修改,从而实现数据的自动更…...

FPGA实现GTP光口数据回环传输,基于Aurora 8b/10b编解码架构,提供2套工程源码和技术支持
目录 1、前言工程概述免责声明 2、相关方案推荐我已有的所有工程源码总目录----方便你快速找到自己喜欢的项目我这里已有的 GT 高速接口解决方案 3、工程详细设计方案工程设计原理框图用户数据发送模块基于GTP高速接口的数据回环传输架构GTP IP 简介GTP 基本结构GTP 发送和接收…...

Linux网络 UDP socket
背景知识 我们知道, IP 地址用来标识互联网中唯一的一台主机, port 用来标识该主机上唯一的一个网络进程,IPPort 就能表示互联网中唯一的一个进程。所以通信的时候,本质是两个互联网进程代表人来进行通信,{srcIp&…...

如何持续优化呼叫中心大模型呼入机器人的性能?
如何持续优化呼叫中心大模型呼入机器人的性能? 原作者:开源呼叫中心FreeIPCC,其Github:https://github.com/lihaiya/freeipcc 持续优化呼叫中心大模型呼入机器人的性能是一个复杂而细致的过程,它涉及到数据、模型结构…...

鸿蒙项目云捐助第四讲鸿蒙App应用的登陆注册页实现
根据app的操作流程可以知道,当启动页启动后,点击启动页中的页面就进入到了登录页。本讲就是针对于登录注册页的实现,实现的界面参考下图。 这里根据这个素材的参考实现鸿蒙Next云捐助的登录页。 一、鸿蒙Next云捐助登录页的实现 在项目中继…...

Windows本地搭建Redis集群(集群模式)
手打不易,如果转摘,请注明出处! 注明原文:https://blog.csdn.net/q258523454/article/details/144477957 前言 Redis版本:redis 5.0.14.1 Windows版本:Windows10 本文只讲集群模式 1. 安装Redis 1.1 …...

使用FastGPT制做一个AI网站日志分析器
越来越的多网站面临每天上千次的扫描和各类攻击,及时发现攻击IP,并有效的屏蔽不良访问成为网站安全的重要保障,这里我们使用AI来完成对网站日志的日常分析。 我们来使用FastGPT来制做一个AI网站日志析器,下面就开始: …...

探索 Echarts 绘图:数据可视化的奇妙之旅
目录 一、Echarts 初印象 二、搭建 Echarts 绘图环境 三、绘制第一个图表:柱状图的诞生 四、图表的美化与定制:让数据更具吸引力 1. 主题切换:一键变换风格 2. 颜色调整:色彩搭配的艺术 3. 标签与提示框:丰富信…...

网络基础(IP和端口)
网络连接的核心-TCP/IP体系结构(IP和端口) 什么是IP地址 1.IP地址是电子设备(计算机)在互联网上的唯一标识 2.用来在互联网中寻找电脑 IP 地址就像是你家的地址一样,不过它是在网络世界里用来找到一台电脑或者其他网…...

UE4与WEB-UI通信
前端HTML代码 <!DOCTYPE html><html><head><meta charset"utf-8"><meta name"viewport" content"widthdevice-width, initial-scale1"><title>test web ui</title><script src"https://cdn.b…...

前缀和与差分算法详解
定义 前缀和是一种数据预处理技术,它指的是从数组的第一个元素开始,到当前元素为止的所有元素的和。这种技术可以快速计算任意区间内元素的和,而不需要每次都从头开始累加。 差分则是前缀和的逆运算,它主要用于处理对数组某个区…...

《深入探究:C++ 在多方面对 C 语言实现的优化》
目录 一、C 在 C 上进行的优化二、C 关键字(C 98)三、C 的输入输出1. cin 和 cout 的使用2. cin、cout 和 scanf()、printf() 的区别 三、命名空间1. 命名空间的使用2. 嵌套命名空间3. 在多个头文件中使用相同的命名空间 四、函数缺省值1. 缺省值的使用2…...

React 第十六节 useCallback 使用详解注意事项
useCallback 概述 1、useCallback 是在React 中多次渲染缓存函数的 Hook,返回一个函数的 memoized的值; 2、如果多次传入的依赖项不变,那么多次定义的时候,返回的值是相同的,防止频繁触发更新; 3、多应用在 父组件为函…...

使用C#和OPenCV实现圆形检测
文章目录 霍夫变换使用 OpenCV 和 C# 实现圆形检测 霍夫变换 在计算机视觉中,圆形检测是一个常见且有用的任务,特别是在物体识别、图像分析和图形处理等领域。OpenCV 是一个强大的开源计算机视觉库,它提供了许多工具来实现不同的图像处理功能…...

评估一套呼叫中心大模型呼入机器人的投入回报比?
评估一套呼叫中心大模型呼入机器人的投入回报比? 原作者:开源呼叫中心FreeIPCC,其Github:https://github.com/lihaiya/freeipcc 评估一套呼叫中心大模型呼入机器人的投入回报比(ROI),是一个多…...

十八、Label 和 Selector
Label 是键值对,用来标识 Kubernetes 资源(如 Pod、Node、Service 等)的属性。它们并不直接影响资源的行为,但可以帮助用户快速组织、查询和操作这些资源。标签可以用于选择、过滤和分组。 Label: 标签对 k8s 中各种资源进行分类、分组,如Pod和节点进行分组。通过添加kev…...

实现按键按下(低电平)检测到下降沿
按照流程进行编程 步骤1: 初始化函数 包括时基工作参数配置 输入通道配置 更新中断使能 使能捕获、捕获中断及计数器 HAL_TIM_IC_Init(&ic_handle) //时基参数配置 HAL_TIM_IC_ConfigChannel(&ic_handle,&ic_config,TIM_CHANNEL_2) //输…...

解析 SSM 垃圾分类系统,助力生态平衡
前 言 垃圾分类系统,传统的垃圾分类系统模式还处于线下管理阶段,管理效率极低。随着垃圾分类系统信息的不断增多,传统基于线下管理模式已经无法满足当前用户需求,随着信息化时代的到来。通过该系统的设计,管理员可以管…...

软件工程 设计的复杂性
复杂性代表事件或事物的状态,它们具有多个相互关联的链接和高度复杂的结构。在软件编程中,随着软件设计的实现,元素的数量以及它们之间的相互联系逐渐变得庞大,一下子变得难以理解。 如果不使用复杂性指标和度量,软件…...

Nginx 限制只能白名单 uri 请求的配置
实际生产项目中,大多数时候我们会将后端的 http 接口通过前置 nginx 进行反向代理,对互联网用户提供服务。往往我们后端服务所能提供的接口服务是大于互联网用户侧的实际请求的接口地址数量的(例如后端服务一共有100个api接口,经过…...

QT c++ 同时使用sqlite 和mysql数据库的问题
在项目开发中,同时使用了sqlite 和mysql数据库,分开这两部分运行功能都正常,但是一起运行,就异常,sqlite部分不能使用。 现象:出现如下提示 QSqlDatabasePrivate::addDatabase: duplicate connection nam…...

redis集群 服务器更换ip,怎么办,怎么更换redis集群的ip
redis集群 服务器更换ip,怎么办,怎么更换redis集群的ip 1、安装redis三主三从集群2、正常状态的redis集群3、更改redis集群服务器的ip 重启服务器 集群会down4、更改redis集群服务器的ip 重启服务器 集群down的原因5、更改redis集群服务器的ip后…...