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

7.4.分块查找
一.分块查找的算法思想: 1.实例: 以上述图片的顺序表为例, 该顺序表的数据元素从整体来看是乱序的,但如果把这些数据元素分成一块一块的小区间, 第一个区间[0,1]索引上的数据元素都是小于等于10的, 第二…...

树莓派超全系列教程文档--(61)树莓派摄像头高级使用方法
树莓派摄像头高级使用方法 配置通过调谐文件来调整相机行为 使用多个摄像头安装 libcam 和 rpicam-apps依赖关系开发包 文章来源: http://raspberry.dns8844.cn/documentation 原文网址 配置 大多数用例自动工作,无需更改相机配置。但是,一…...
基于服务器使用 apt 安装、配置 Nginx
🧾 一、查看可安装的 Nginx 版本 首先,你可以运行以下命令查看可用版本: apt-cache madison nginx-core输出示例: nginx-core | 1.18.0-6ubuntu14.6 | http://archive.ubuntu.com/ubuntu focal-updates/main amd64 Packages ng…...
Qwen3-Embedding-0.6B深度解析:多语言语义检索的轻量级利器
第一章 引言:语义表示的新时代挑战与Qwen3的破局之路 1.1 文本嵌入的核心价值与技术演进 在人工智能领域,文本嵌入技术如同连接自然语言与机器理解的“神经突触”——它将人类语言转化为计算机可计算的语义向量,支撑着搜索引擎、推荐系统、…...
【Java学习笔记】BigInteger 和 BigDecimal 类
BigInteger 和 BigDecimal 类 二者共有的常见方法 方法功能add加subtract减multiply乘divide除 注意点:传参类型必须是类对象 一、BigInteger 1. 作用:适合保存比较大的整型数 2. 使用说明 创建BigInteger对象 传入字符串 3. 代码示例 import j…...

无人机侦测与反制技术的进展与应用
国家电网无人机侦测与反制技术的进展与应用 引言 随着无人机(无人驾驶飞行器,UAV)技术的快速发展,其在商业、娱乐和军事领域的广泛应用带来了新的安全挑战。特别是对于关键基础设施如电力系统,无人机的“黑飞”&…...
OD 算法题 B卷【正整数到Excel编号之间的转换】
文章目录 正整数到Excel编号之间的转换 正整数到Excel编号之间的转换 excel的列编号是这样的:a b c … z aa ab ac… az ba bb bc…yz za zb zc …zz aaa aab aac…; 分别代表以下的编号1 2 3 … 26 27 28 29… 52 53 54 55… 676 677 678 679 … 702 703 704 705;…...

抽象类和接口(全)
一、抽象类 1.概念:如果⼀个类中没有包含⾜够的信息来描绘⼀个具体的对象,这样的类就是抽象类。 像是没有实际⼯作的⽅法,我们可以把它设计成⼀个抽象⽅法,包含抽象⽅法的类我们称为抽象类。 2.语法 在Java中,⼀个类如果被 abs…...

自然语言处理——文本分类
文本分类 传统机器学习方法文本表示向量空间模型 特征选择文档频率互信息信息增益(IG) 分类器设计贝叶斯理论:线性判别函数 文本分类性能评估P-R曲线ROC曲线 将文本文档或句子分类为预定义的类或类别, 有单标签多类别文本分类和多…...
鸿蒙HarmonyOS 5军旗小游戏实现指南
1. 项目概述 本军旗小游戏基于鸿蒙HarmonyOS 5开发,采用DevEco Studio实现,包含完整的游戏逻辑和UI界面。 2. 项目结构 /src/main/java/com/example/militarychess/├── MainAbilitySlice.java // 主界面├── GameView.java // 游戏核…...