当前位置: 首页 > news >正文

应用层自定义协议序列与反序列化

目录

一、网络版计算器

二、网络版本计算器实现

2.1源代码

2.2测试结果


一、网络版计算器

应用层定义的协议:

应用层进行网络通信能否使用如下的协议进行通信呢?

在操作系统内核中是以这种协议进行通信的,但是在应用层禁止以这种协议进行通信,原因如下:

  1. 我们写的服务端程序是在Linux系统上运行的,但是客户端程序不一定是在Linux系统上运行的,可能是在Windos系统上运行的,服务端在不同平台上运行,不同的平台下内存对齐等等可能不同,那么对同一个结构体定义出来的结构体变量大小可能不同。还有服务端程序是由C++语言写的,客户端程序可能是由其他语言写的,如:Java语言、Python语言写的,那么类型大小可能不一样。
  2. 即使对内存对齐问题做出解决,但是我们的程序随时随地根据需求需要做出更改,那么之前写的协议就需要做出改变,重新进行内存等问题的处理,重新进行测试,出现问题的可能性很大,这样写出来的代码可扩展性很差。

操作系统内核能够以这种方式进行通信的原因:操作系统都是用C语言写的,操作系统一旦写好,基本不会做改变,使用这种协议进行本地通信实现起来简单。

哪如何进行网络通信呢?

第一种约定方案:客户端发送一个形如"1+1"的字符串,中间不存在空格

第二种约定方案:定义结构体来表示我们需要交互的信息, 发送数据时将这个结构体按照一个规则转换成字符串, 接收到数据的时候再按照相同的规则把字符串转化回结构体。这个过程叫做 “序列化” 和 "反序列化。

  • 传输层和网络层是操作系统部分,数据链路层对应的是驱动。
  • 一个fd代表一个链接,一个链接有两个缓冲区,read()、write()、send()、recv()这些函数本质是拷贝函数,将内容拷贝到换冲区,从缓冲区中拷贝内容。
  • 发送数据的本质:是从发送方的发送缓冲区把数据通过协议栈和网络拷贝给接受方的接收缓冲区,这也就是为什么TCP协议是全双工通信模式。
  • 应用层将数据拷贝给传输层,数据什么时候发、一次发多少、出错了怎么办应用层不用管由操作系统来决定,这也就是TCP称为传输控制协议的原因。
  • 传输数据过程其实就是生产消费模型,发送和接收缓冲区就是临界资源,read()阻塞就是接收缓冲区是空的,write()阻塞就是发送缓冲区满了。

二、网络版本计算器实现

序列化和反序列化的工具:Jsoncpp用于将数据序列化为字符串的C++库

安装Jsoncpp库

ubuntu: sudo apt-get install libjsoncpp-dev
Centos: sudo yum install jsoncpp-devel

  • 对于系统头文件默认是在/usr/include目录下找,要想使用必须这样包含头文件

     #include <jsoncpp/json/json.h>
    

【第一次测试】

这个报错是链接报错,gcc、g++默认是认识C/C++库,但不认识第三方提供的库,在makefile文件中添加

-ljsoncpp,告诉编译器指定到那个库中去找。

2.1源代码

SeverMain.cc(服务端入口)

#include <iostream>
#include <functional>#include "Sever.hpp"
#include "IoService.hpp"
#include "Calculate.hpp"int main()
{Scream();uint16_t port = 8888;Calculate cal;IoService ioService(std::bind(&Calculate::Operation, &cal, std::placeholders::_1));Sever sever(std::bind(&IoService::IoExecute, &ioService, std::placeholders::_1, std::placeholders::_2),port);sever.Init();sever.Loop();return 0;
}

Sever.hpp(服务端)

#pragma once
#include <functional>#include "Socket.hpp"
#include "Log.hpp"
#include "InetAddr.hpp"
#include "IoService.hpp"using namespace socket_n;
const static int gport = 8888;
using ioService_t = std::function<void(SockPtr, InetAddr &)>; // 可调用对象的类型,Io业务处理类型class Sever
{
public:Sever(ioService_t ioService, uint16_t port = gport): _port(port), _ifRunning(false), _listenSocket(), _ioService(ioService){}void Init(){// 创建监听套接字、绑定、设置监听状态_listenSocket.CreatListenSocket(_port);}struct ThreadData{ThreadData(SockPtr sockfd, InetAddr addr, Sever *pSever): _sockfd(sockfd), _addr(addr), _pSever(pSever){}SockPtr _sockfd;InetAddr _addr;Sever *_pSever;};void Loop(){_ifRunning = true;while (_ifRunning){InetAddr client_addr;SockPtr SockfdSmartPtr = _listenSocket.Accept(client_addr);if (SockfdSmartPtr == nullptr){LOG(WARNING, "accept error\n");continue;}LOG(INFO, "get a new link, client info: %s\n", client_addr.AddrStr().c_str());// 多线程处理业务pthread_t tid;// 智能指针和继承???????ThreadData *td = new ThreadData(SockfdSmartPtr, client_addr, this); // td必须是动态开辟出来的pthread_create(&tid, nullptr, Execute, td);}_ifRunning = false;}static void *Execute(void *args){pthread_detach(pthread_self()); // 将自己和主线程分离ThreadData *td = static_cast<ThreadData *>(args);td->_pSever->_ioService(td->_sockfd, td->_addr); // 回调交互业务函数,执行交互业务td->_sockfd->Close();                            // 业务处理完,也不再进行网络通信,关闭该套接字delete td;return nullptr;}~Sever(){}private:bool _ifRunning;uint16_t _port;TcpSocket _listenSocket;ioService_t _ioService;
};

Socket.hpp(套接字)

#pragma once
#include <iostream>
#include <cstring>
#include <string>
#include <functional>
#include <memory>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#include "Log.hpp"
#include "InetAddr.hpp"namespace socket_n
{// 父类提供接口,子类实现接口const static int gsockfd = -1;const static int gbacklog = 8;enum{SOCKET_ERROR = 1,BIND_ERROR,LISTEN_ERROR};class Socket;using SockPtr = std::shared_ptr<Socket>; // 父类中也用到了,所以在父类前面声明class Socket{public:virtual void CreatSocket() = 0;virtual void BindSocket(uint16_t port) = 0;virtual void SetListenStatus(int balcklog = gbacklog) = 0;virtual SockPtr Accept(InetAddr &client_addr) = 0;virtual bool Connect(const std::string &sever_ip, uint16_t sever_port) = 0;virtual int Sockfd() = 0;virtual void Close() = 0;virtual ssize_t Recv(std::string &out) = 0;virtual ssize_t Send(const std::string &in) = 0;// 创建监听套接字void CreatListenSocket(uint16_t port){CreatSocket();BindSocket(port);SetListenStatus();}// 客户端套接字bool CreatClientSocket(const std::string &sever_ip, uint16_t sever_port){CreatSocket();return Connect(sever_ip, sever_port);}};class TcpSocket : public Socket{public:TcpSocket(int sockfd = gsockfd): _sockfd(sockfd){}TcpSocket(TcpSocket &socket): _sockfd(socket._sockfd){}void CreatSocket() override{_sockfd = socket(AF_INET, SOCK_STREAM, 0);if (_sockfd < 0){LOG(FATAL, "socket creat error\n");exit(SOCKET_ERROR);}LOG(INFO, "socket creat success,sockfd: %d\n", _sockfd);}void BindSocket(uint16_t port) override{struct sockaddr_in local;memset(&local, 0, sizeof(local));local.sin_family = AF_INET;local.sin_port = htons(port);local.sin_addr.s_addr = INADDR_ANY;if (bind(_sockfd, (struct sockaddr *)&local, sizeof(local)) < 0){LOG(FATAL, "bind error\n");exit(BIND_ERROR);}LOG(INFO, "bind success\n");}void SetListenStatus(int balcklog) override{if (listen(_sockfd, balcklog) < 0){LOG(FATAL, "set listen error\n");exit(LISTEN_ERROR);}LOG(INFO, "listen success\n");}SockPtr Accept(InetAddr &client_addr) override{struct sockaddr_in client;socklen_t len = sizeof(client);int sockfd = accept(_sockfd, (struct sockaddr *)&client, &len);if (sockfd < 0){LOG(WARNING, "accept error\n");return nullptr; // 返回一个用nullptr构造的智能指针}client_addr = InetAddr(client);LOG(INFO, "get a new link, client info: %s\n", client_addr.AddrStr().c_str());return std::make_shared<TcpSocket>(sockfd); // C++14}bool Connect(const std::string &sever_ip, uint16_t sever_port) override{struct sockaddr_in server;memset(&server, 0, sizeof(server));server.sin_family = AF_INET;server.sin_port = htons(sever_port);::inet_pton(AF_INET, sever_ip.c_str(), &server.sin_addr);int n = ::connect(_sockfd, (struct sockaddr *)&server, sizeof(server));if (n < 0){return false;}return true;}int Sockfd() override{return _sockfd;}void Close() override{if (_sockfd > 0)close(_sockfd);}ssize_t Recv(std::string &out) override{char inbuffer[1024];ssize_t n = recv(_sockfd, inbuffer, sizeof(inbuffer) - 1, 0);if (n > 0){inbuffer[n] = 0;out = inbuffer;}return n;}ssize_t Send(const std::string &in) override{return send(_sockfd, in.c_str(), in.size(), 0);}private:int _sockfd;};
}

IoSever.hpp(IO交互)

#pragma once
#include <memory>
#include <functional>#include "InetAddr.hpp"
#include "Socket.hpp"
#include "Log.hpp"
#include "Protocol.hpp"using namespace socket_n;
using process_t = std::function<std::shared_ptr<Response>(std::shared_ptr<Request>)>;
class IoService
{
public:IoService(process_t process): _process(process){}void IoExecute(SockPtr sockPtr, InetAddr &addr){std::string packageStream;std::shared_ptr<Request> req_ptr = std::make_shared<Request>();std::string recvMessage;std::string sendMessage;while (true){// 读内容ssize_t n = sockPtr->Recv(recvMessage);// 数据读取出错或者没数据了,直接break,关闭套接字,停止对该客户端的服务// 客户端可以重新建立连接if (n <= 0){LOG(ERROR, "read error or rend end: %s\n", addr.AddrStr().c_str());break;}packageStream += recvMessage;// 提取一个完整的报文std::string jsonStr = DeCode(packageStream);if (jsonStr == "")continue;// 反序列化req_ptr->Deserialize(jsonStr);// 计算auto res_ptr = _process(req_ptr);// 序列化res_ptr->Serialize(sendMessage);// 添加报头sendMessage = EnCode(sendMessage);// 向套接字中写sockPtr->Send(sendMessage);}}~IoService(){}private:process_t _process;
};

Protocol.hpp(计算器协议)

#pragma once
#include <string>
#include <jsoncpp/json/json.h>// 应用层协议完整的报头+报文格式
//"len\r\n{jsonStr}\r\n" —— "len":报文的长度;"\r\n":区分len和报文
// 使用"\r\n"作为分割原因是在Debug测试时打印出来的结果:一行是len、一行是报文的形式,也可以用其他作为分割
//"{jsonStr}\r\n"能不能只以"\r\n"作为分割呢?
// 不能因为报文中有可能也有\r\n,如果查找\r\n,取其前面的内容作为报文是不行的
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;
}
//"le"、"len\r"
//"len\r\n{js"
//"len\r\n{jsonStr}\r\nlen\r\n{js"
// 从传的流中提取出一个完整的报文,如果流中没有一个完整的报文返回空,如果有返回一个完整的报文字符串
std::string DeCode(std::string &packageStream)
{size_t n = packageStream.find(sep);if (n == std::string::npos)return "";std::string numStr = packageStream.substr(0, n);int len = std::stoi(numStr);// 当前这个完整报文的长度int total = numStr.size() + len + 2 * sep.size();if (packageStream.size() < total)return "";int pos = n + sep.size();std::string jsonStr = packageStream.substr(pos, len);// 提取到一个完整的jsonStr串,删掉packageStream中头部字段packageStream.erase(0, total);return jsonStr;
}// 请求
class Request
{
public:Request(int x = 1, int y = 1, char oper = '+'): _x(x), _y(y), _oper(oper){}// 序列化:将结构化字段转换成字符串void Serialize(std::string &out){// 先定义一个中间值,Value类型的对象rootJson::Value root;root["x"] = _x; // 一个键值对应一个value值,value可以是任意类型,包括对象root["y"] = _y;root["oper"] = _oper;// 再定义一个FastWriter类型的对象,使用其内部的write()方法将中间值root序列化成字符串Json::FastWriter writer;std::string s = writer.write(root);out = s;}// 反序列化:将字符串转换成结构化字段bool Deserialize(std::string &in){Json::Value root;// 将序列化后的字符串,转化成Value的中间值Json::Reader reader;bool res = reader.parse(in, root);if (res){_x = root["x"].asInt(); // 将root中的"x"对应的value值作为正数给给_x_y = root["y"].asInt();_oper = root["oper"].asInt();return true;}return false;}int RetX(){return _x;}int RetY(){return _y;}char RetOper(){return _oper;}~Request(){}// private:int _x;int _y;char _oper;
};// 回应
class Response
{
public:Response(): _result(2), _code(0), _describe("Calculation successful"){}// 序列化void Serialize(std::string &out){// 先定义一个中间值,Value类型的对象rootJson::Value root;root["result"] = _result; // 一个键值对应一个value值,value可以是任意类型,包括对象root["code"] = _code;root["describe"] = _describe;// 再定义一个FastWriter类型的对象,使用其内部的write()方法将中间值root序列化成字符串Json::FastWriter writer;std::string s = writer.write(root);out = s;}// 反序列化bool Deserialize(std::string &in){Json::Value root;// 将序列化后的字符串,转化成Value的中间值Json::Reader reader;bool res = reader.parse(in, root);if (res){_result = root["result"].asInt(); // 将root中的"x"对应的value值作为正数给给_x_code = root["code"].asInt();_describe = root["describe"].asString();return true;}return false;}~Response(){}// private:int _result;int _code;             // 结果码0:计算成功 1:除0错误 2:其他非法操作std::string _describe; // 对结果码的描述
};

Calculate.hpp(计算功能)

#pragma once
#include <memory>
#include <climits>#include "Protocol.hpp"class Calculate
{
public:
Calculate()
{}
std::shared_ptr<Response> Operation(std::shared_ptr<Request> reqPtr)
{int x = reqPtr->RetX();int y = reqPtr->RetY();char oper = reqPtr->RetOper();std::shared_ptr<Response> resPtr = std::make_shared<Response>();switch (oper){case '+':{resPtr->_result = x + y;break;}case '-':{resPtr->_result = x - y;break;}case '*':{resPtr->_result = x * y;break;}case '/':{if (y == 0){resPtr->_result = INT_MAX;resPtr->_code = 1;resPtr->_describe = "Division by zero error";}else{resPtr->_result = x / y;}break;}default:{resPtr->_result = INT_MIN;resPtr->_code = 2;resPtr->_describe = "Other illegal operations";break;}}return resPtr;
}
~Calculate()
{}private:
};

Log.hpp(日志)

#pragma once
#include <string>
#include <ctime>
#include <fstream>
#include <cstring>
#include <sys/types.h>
#include <unistd.h>
#include <stdarg.h>
#include <pthread.h>#define SCREAM 1
#define FILE 2
enum 
{DEBUG = 1,INFO,WARNING,ERROR,FATAL
};
//日志消息 [日志等级][pid][filename][filenumber][time] 日志内容
class LMessage
{
public:std::string _level;//信息等级pid_t _pid;//进程idstd::string _filename;//所在文件int _filenumber;//所在文件行号std::string _cur_time; //打印时间std::string _message_info;//日志内容
};
//获取当前时间
std::string GetCurTime()
{//从过去的那一个时刻到现在累计的秒数time_t now = time(nullptr);//将时间戳转换成年月日时分秒的字符串struct tm* cur_time = localtime(&now);char buffer[100];snprintf(buffer, sizeof(buffer), "%d-%02d-%02d %02d:%02d:%02d", cur_time->tm_year+1900,cur_time->tm_mon+1,cur_time->tm_mday,cur_time->tm_hour,cur_time->tm_min,cur_time->tm_sec);return buffer;
}pthread_mutex_t mutex1 = PTHREAD_MUTEX_INITIALIZER;
pthread_mutex_t mutex2 = PTHREAD_MUTEX_INITIALIZER;
const std::string g_file = "./log.txt";
//日志
class Log
{
private:void FlushToScream(LMessage& message){//对指定的锁进行加锁pthread_mutex_lock(&mutex1); //[日志等级][pid][filename][filenumber][time] 日志内容printf("[%s][%d][%s][%d][%s]%s",message._level.c_str(), message._pid,message._filename.c_str(),message._filenumber,message._cur_time.c_str(),message._message_info.c_str());//对指定的锁进行解锁pthread_mutex_unlock(&mutex1);}void FlushToFile(LMessage& message){//对指定的锁进行加锁pthread_mutex_lock(&mutex1); //std::ofstream out(_file)//每次写入前先前会先清空文件std::ofstream out(_file, std::ios::app);//向文件追加写入if(!out.is_open())return ;char buffer[1024];snprintf(buffer, sizeof(buffer), "[%s][%d][%s][%d][%s]%s",message._level.c_str(), message._pid,message._filename.c_str(),message._filenumber,message._cur_time.c_str(),message._message_info.c_str());out.write(buffer, strlen(buffer));out.close();//对指定的锁进行解锁pthread_mutex_unlock(&mutex1);}void Fussh(LMessage& message){if(_type == SCREAM){//向显示器中打印FlushToScream(message);}else if(_type == FILE){//向文件中打印FlushToFile(message);}}
public:Log()//默认是向显示器中打印:_type(SCREAM),_file(g_file){}//为了方便将等级名称输出到文件/显示屏中,将数字转换成字符串const std::string LevelToString(int level){switch(level){case DEBUG:return "DEBUG";case INFO:return "INFO";case ERROR:return "ERROR";case FATAL:return "FATAL";default:return "UNKNOW";//表示未知的}}//通过log.LogMessage("xx",12,INFO,"ctear %d thread success",pid)这种形式将消息写进日志中//通过外部传一个消息,由于外部有时会需要传一个可变参数,message_info在传的时候需要设计成可变参数void LogMessage(const std::string filename, int filenumber, int level, const char* format, ...){LMessage message;message._level = LevelToString(level);message._pid = getpid();message._filename = filename;message._filenumber = filenumber;message._cur_time = GetCurTime();//定义一个apva_list ap;//初始化ap,让ap指向可变参数va_start(ap,format);char info[1024];//将格式化形式传进来,可变参数传进来,自动将转换成字符串放到字符数组中vsnprintf(info, sizeof(info), format, ap);//销毁apva_end(ap);message._message_info = info;//将消息写入到文件或显示器;Fussh(message);}    ~Log(){pthread_mutex_destroy(&mutex1);pthread_mutex_destroy(&mutex2);}void FlusshScream(){_type = SCREAM;}void FlusshFile(){_type = FILE;}
private:int _type;const std::string _file;
};Log lg;
#define LOG(level, format, ...) do{ lg.LogMessage(__FILE__, __LINE__, level, format, ##__VA_ARGS__); }while(0)
#define Scream() do{ lg.FlusshScream(); }while(0)
#define File() do{ lg.FlusshFile(); }while(0)

InetAddr.hpp

#pragma once#include <iostream>
#include <string>
#include <sys/types.h>
#include <sys/socket.h>
#include <arpa/inet.h>
#include <netinet/in.h>class InetAddr
{
public:InetAddr(){}InetAddr(const struct sockaddr_in &addr) : _addr(addr){_port = ntohs(addr.sin_port);char ip_buf[32];::inet_ntop(AF_INET, &addr.sin_addr, ip_buf, sizeof(ip_buf));_ip = ip_buf;}bool operator==(const InetAddr &addr){return (this->_ip == addr._ip && this->_port == addr._port);}std::string Ip(){return _ip;}uint16_t Port(){return _port;}struct sockaddr_in Addr(){return _addr;}std::string AddrStr(){return _ip + ":" + std::to_string(_port);}~InetAddr(){}private:std::string _ip;uint16_t _port;struct sockaddr_in _addr;
};

ClientMain.cc(客户端入口)

#include <iostream>
#include <string>#include <sys/types.h>
#include <sys/socket.h>
#include <netinet/in.h>
#include <arpa/inet.h>
#include <unistd.h>#include "Socket.hpp"
#include "Protocol.hpp"
#include "InetAddr.hpp"using namespace socket_n;int main(int argc, char *argv[])
{if (argc != 3){std::cout << argv[0] << "serve-ip serve-port" << std::endl;exit(0);}std::string severIp = argv[1];uint16_t severPort = std::stoi(argv[2]);// 1.创建socket链接远端服务器SockPtr socketPtr = std::make_shared<TcpSocket>();if (!socketPtr->CreatClientSocket(severIp, severPort)){std::cout << "creat or connect error" << std::endl;exit(1);}std::shared_ptr<Request> reqPtr = std::make_shared<Request>();std::shared_ptr<Response> resPtr = std::make_shared<Response>();std::string packageStream;while (true){// 设置requestreqPtr->_x = 888;reqPtr->_y = 1;reqPtr->_oper = '*';// 序列化std::string sendMessage;reqPtr->Serialize(sendMessage);// 添加报头sendMessage = EnCode(sendMessage);// 向套接字中写socketPtr->Send(sendMessage);while (true){// 接收信息std::string recvMessage;ssize_t n = socketPtr->Recv(recvMessage);if (n <= 0)break;packageStream += recvMessage;// 提取一个完整的报文std::string jison = DeCode(packageStream);if (jison == "")continue;std::cout << "jison string: " << std::endl;std::cout << jison << std::endl;// 反序列化resPtr->Deserialize(jison);// 打印resultstd::cout << "result: " << resPtr->_result<< "code: " << resPtr->_code<< "describe: " << resPtr->_describe << std::endl;}sleep(2);}socketPtr->Close();return 0;
}

2.2测试结果

相关文章:

应用层自定义协议序列与反序列化

目录 一、网络版计算器 二、网络版本计算器实现 2.1源代码 2.2测试结果 一、网络版计算器 应用层定义的协议&#xff1a; 应用层进行网络通信能否使用如下的协议进行通信呢&#xff1f; 在操作系统内核中是以这种协议进行通信的&#xff0c;但是在应用层禁止以这种协议进行…...

大数据:数字时代的驱动力

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 随着互联网和技术的迅猛发展,数据已经成为现代社会最宝贵的资源之一。大数据(Big Data)作为一种全新的信息资源,正以前所未有的方式改变着各个行业的运作模式,推动着社会的进步与创新。无论是金…...

java学习之数据结构:二、链表

本节介绍链表 目录 1.什么是链表 1.1链表定义 1.2链表分类 2.链表实现 2.1创建链表 1&#xff09;手动创建 2&#xff09;创建链表类进行管理链表的相关操作 2.2添加元素 1&#xff09;头插法 2&#xff09;尾插法 3&#xff09;任意位置插入 2.3删除 2.4查找 1&…...

评估动态化烹饪工艺与营养实训室建设要点

在全民健康意识显著提升、健康饮食理念深度普及的时代背景下&#xff0c;烹饪工艺与营养实训室建设要点已不再局限于传统单一的技能训练模式。需以行业需求为导向&#xff0c;通过功能集成化设计推动革新 —— 将食品安全防控、营养科学分析、智能烹饪技术、餐饮运营管理等多元…...

Java学习手册:关系型数据库基础

一、关系型数据库概述 关系型数据库是一种基于关系模型的数据库&#xff0c;它将数据组织成一个或多个表&#xff08;或称为关系&#xff09;&#xff0c;每个表由行和列组成。每一列都有一个唯一的名字&#xff0c;称为属性&#xff0c;表中的每一行是一个元组&#xff0c;代…...

吾爱出品 [Windows] EndNote 21.5.18513 汉化补丁

[Windows] EndNote 链接&#xff1a;https://pan.xunlei.com/s/VOPLLs6DqKNz-EoBSWVRTSmGA1?pwd9isc# Thomson Scientific公司推出了2025&#xff0c;本次的endnote21大概率是最后一个版本啦&#xff0c;现已决定进行更新。 本次采用的环境为python3.12&#xff0c;win11&am…...

Sentinel学习

sentinel是阿里巴巴研发的一款微服务组件&#xff0c;主要为用户提供服务保护&#xff0c;包括限流熔断等措施 &#xff08;一&#xff09;主要功能 流量控制&#xff08;限流&#xff09;&#xff1a;比如限制1s内有多少请求能到达服务器&#xff0c;防止大量请求打崩服务器…...

【中间件】brpc_基础_execution_queue

execution_queue 源码 1 简介 execution_queue.h 是 Apache BRPC 中实现 高性能异步任务执行队列 的核心组件&#xff0c;主要用于在用户态线程&#xff08;bthread&#xff09;中实现任务的 异步提交、有序执行和高效调度。 该模块通过解耦任务提交与执行过程&#xff0c;提…...

Servlet(二)

软件架构 1. C/S 客户端/服务器端 2. B/S 浏览器/服务器端&#xff1a; 客户端零维护&#xff0c;开发快 资源分类 1. 静态资源 所有用户看到相同的部分&#xff0c;如&#xff1a;html,css,js 2. 动态资源 用户访问相同资源后得到的结果可能不一致&#xff0c;如&#xff1a;s…...

如何提升个人的思维能力?

提升个人的逻辑思维能力是一个系统性工程&#xff0c;需要长期训练和科学方法。以下是分阶段、可操作的详细建议&#xff0c;涵盖理论基础、日常训练和实战应用&#xff1a; 一、构建逻辑基础认知 1. 学习逻辑学核心理论 入门读物&#xff1a;《简单的逻辑学》麦克伦尼&am…...

[UVM]UVM中reg_map的作用及多个rem_map的使用案例

UVM中reg_map的作用及多个rem_map的使用案例 摘要:在 UVM (Universal Verification Methodology) 中,寄存器模型是用于验证 DUT (Design Under Test) 寄存器行为的重要工具。UVM 寄存器模型中的 uvm_reg_map(简称 reg_map)是寄存器模型的核心组成部分之一,用于定义…...

重新构想E-E-A-T:提升销售与搜索可见性的SEO策略

在2025年的数字营销环境中&#xff0c;谷歌的E-E-A-T&#xff08;经验、专业性、权威性、可信度&#xff09;已成为SEO和内容营销的核心支柱。传统的E-E-A-T优化方法通常聚焦于展示作者资质或获取反向链接&#xff0c;但这些策略可能不足以应对AI驱动的搜索和日益挑剔的用户需求…...

AI 采用金字塔(Sohn‘s AI Adoption Pyramid)

这张图是 Sohn 的 AI 采用金字塔&#xff08;Sohn’s AI Adoption Pyramid&#xff09; &#xff0c;用于描述不同程度的 AI 应用层次&#xff0c;各层次意义如下&#xff1a; 金字塔层级 Level 1&#xff1a;业务角色由人类主导&#xff0c;AI 起辅助作用&#xff0c;如 AI …...

影刀RPA中新增自己的自定义指令

入门到实战明细 1. 影刀RPA自定义指令概述 1.1 定义与作用 影刀RPA的自定义指令是一种强大的功能&#xff0c;旨在提高流程复用率&#xff0c;让用户能够个性化定制指令&#xff0c;实现流程在不同应用之间的相互调用。通过自定义指令&#xff0c;用户可以将常用的、具有独立…...

驱动总裁v2.19(含离线版)驱动工具软件下载及安装教程

1.软件名称&#xff1a;驱动总裁 2.软件版本&#xff1a;2.19 3.软件大小&#xff1a;602 MB 4.安装环境&#xff1a;win7/win10/win11 5.下载地址&#xff1a; https://www.kdocs.cn/l/cdZMwizD2ZL1?RL1MvMTM%3D 提示&#xff1a;先转存后下载&#xff0c;防止资源丢失&am…...

SQL经典实例

第1章 检索记录 1.1 检索所有行和列 知识点&#xff1a;使用SELECT *快速检索表中所有列&#xff1b;显式列出列名&#xff08;如SELECT col1, col2&#xff09;提高可读性和可控性&#xff0c;尤其在编程场景中更清晰。 1.2 筛选行 知识点&#xff1a;通过WHERE子句过滤符合条…...

2025深圳杯(东三省)数学建模竞赛D题完整分析论文(共36页)(含模型、可运行代码、数据结果)

2025深圳杯数学建模竞赛D题完整分析论文 目录 摘 要 一、问题重述 二、问题分析 三、模型假设 四、符号定义 五、问题一模型的建立与求解 5.1 问题一模型的建立 5.1.1 问题建模背景 5.1.2 特征工程设计 5.1.3 分类模型结构与数学表达 5.2 问题一模型的求…...

大数据技术:从趋势到变革的全景探索

📝个人主页🌹:一ge科研小菜鸡-CSDN博客 🌹🌹期待您的关注 🌹🌹 在数字化时代的浪潮下,大数据已经不再是一个陌生的概念。从日常生活中的社交媒体,到企业决策支持系统,再到公共管理的大数据应用,它正在改变着我们的工作和生活方式。随着技术的进步,传统的数据…...

C++【内存管理】

C语言中的动态内存管理 int main() { int* p2(int*)calloc(4,sizeof(int)); int* p3(int*)realloc(p2,sizeof(int)*10); free(p3); }这里因为扩容数据小,所以是原地扩容,p2p3地址一样,不用free(p2) 如果扩容空间大就不是原地扩容,而是新找一块空间,然后拷贝 C内存管理方式 n…...

【Go类库分享】mcp-go Go搭建MCP服务

【Go类库分享】mcp-go Go搭建MCP服务 介绍 目前Go 生态圈有两个知名的开发 MCP 的库&#xff0c;一个是mark3labs/mcp-go,另一个是metoro-io/mcp-golang。 在介绍常用库之前&#xff0c;先来简单介绍一下mcp协议&#xff1a; MCP全称Model Context Protocol 模型上下文协议&a…...

人工智能发展史 — 物理学诺奖之 Hopfield 联想和记忆神经网络模型

目录 文章目录 目录1982 年&#xff0c;Hopfield 联想和记忆神经网络模型背景知识历史&#xff1a;霍普菲尔德简介神经学&#xff1a;大脑的联想记忆机制物理学&#xff1a;磁性材料的自旋玻璃理论和能量最小值函数 Hopfield 神经网络基本原理记忆存储&#xff08;训练&#xf…...

Docker —— 技术架构的演进

Docker —— 技术架构的演进 技术架构演进总结单机架构优点缺点总结 应用数据分离架构优点缺点总结 应用服务集群架构1. Nginx2. HAProxy3. LVS&#xff08;Linux Virtual Server&#xff09;4. F5 BIG-IP对比总结选型建议 读写分离/主从分离架构1. MyCat简介 2. TDDL&#xff…...

Docker与WSL2如何清理

文章目录 Docker与WSL2如何清理一、docker占据磁盘空间核心原因分析1. WSL2 虚拟磁盘的动态扩展特性2. Docker 镜像分层缓存与未清理资源 二、解决方案步骤 1&#xff1a;清理 Docker 未使用的资源步骤 2&#xff1a;手动压缩 WSL2 虚拟磁盘1. 关闭 WSL2 和 Docker Desktop2. 定…...

单片机嵌入式按键库

kw_btn库说明 本库主要满足嵌入式按键需求&#xff0c;集成了常用的按键响应事件&#xff1a;高电平、低电平、上升沿、下降沿、单击、双击、长按键事件。可以裸机运行&#xff0c;也可以配合实时操作系统运行。 本库开源连接地址&#xff1a;gitee连接 实现思路 本库采用C语…...

多多铃声 7.4| 拥有丰富的铃声曲库,满足不同用户的个性化需求,支持一键设置手机铃声

多多铃声是一款提供丰富铃声资源的应用程序&#xff0c;它拥有广泛的铃声曲库&#xff0c;涵盖各种风格和类型&#xff0c;能够满足不同用户的个性化需求。该应用程序支持分类浏览和热门榜单功能&#xff0c;让用户可以轻松找到当前最流行或自己感兴趣的铃声。此次分享的版本为…...

基于stm32的四旋翼飞行器:MPU6050讲解 · 上(参数读取)

大伙早上好&#xff0c;不知道大伙有没有飞行器情结&#xff0c;就是学习嵌入式就想做一个能飞的东西。小白兔不才&#xff0c;小白兔有啊&#xff0c;所以最近准备做一个简单的飞行器出来&#xff0c;如果失败了&#xff0c;那么这个系列就只能烂尾了&#xff0c;如果成功了&a…...

使用xlwings将excel表中将无规律的文本型数字批量转化成真正的数字

之前我写了一篇文章excel表中将无规律的文本型数字批量转化成真正的数字-CSDN博客 是使用excel自带的操作&#xff0c;相对繁琐。 今天使用xlwings操作&#xff0c;表格如下&#xff08;有真正的数字&#xff0c;也有文本型数字&#xff0c;混在在一起&#xff09;&#xff1…...

linux netlink实现用户态和内核态数据交互

1&#xff0c;内核态代码 #include <linux/module.h> #include <linux/netlink.h> #include <net/sock.h> #define NETLINK_TEST 31 struct sock *nl_sk NULL; static void nl_recv_msg(struct sk_buff *skb) { struct nlmsghdr *nlh; int pid; …...

学习黑客安全基础理论入门

准备安全课程内容 你已安装Kali和相关工具&#xff0c;并希望从基础开始学习安全。为了使课程更加互动&#xff0c;我会提供有趣的文本&#xff0c;并结合可视化内容&#xff0c;可能还会提供一些参考链接。内容方面&#xff0c;我会根据最新的中国网络安全法律作出更新&#…...

探索内容智能化的关键解决方案

北京先智先行科技有限公司拥有三款旗舰产品&#xff0c;分别是“先知大模型”、“先行AI商学院”以及“先知AIGC超级工场”。这三款产品在企业发展过程中扮演着重要角色。  北京先智先行科技有限公司围绕先知大模型等核心要素&#xff0c;构建了完备的业务体系。先知大模型私…...

学习黑客色即是空

二、Day 3 学习目标&#xff08;保真版&#xff09; 一句话目标&#xff1a; 学会用 Asset-Threat-Vulnerability-Risk (ATVR) 四件套给任何系统快速画“风险画像”&#xff0c;并能把它映射到黑客常说的 5 阶段攻击生命周期。 1. 30 分钟理论——ATVR 四件套 概念核心定义参考…...

【Java学习】关于springBoot的自动配置和起步依赖

关于springBoot的起步依赖&#xff1a;解决了spring框架中开发者配置依赖难的问题&#xff0c;各种依赖及版本的不同&#xff0c;可能引发不同的问题&#xff0c;使得开发者的精力大部分可能耗费在非业务代码中。所以springBoot起步依赖解决了各种依赖难的配置问题。 起步依赖…...

【LLaMA-Factory实战】1.3命令行深度操作:YAML配置与多GPU训练全解析

一、引言 在大模型微调场景中&#xff0c;命令行操作是实现自动化、规模化训练的核心手段。LLaMA-Factory通过YAML配置文件和多GPU分布式训练技术&#xff0c;支持开发者高效管理复杂训练参数&#xff0c;突破单机算力限制。本文将结合结构图、实战代码和生产级部署经验&#…...

【Mytais系列】介绍、核心概念

MyBatis 是一款优秀的 持久层框架&#xff0c;它通过简化 JDBC 操作、提供灵活的 SQL 映射能力&#xff0c;成为 Java 开发中处理数据库交互的核心工具之一。以下是 MyBatis 的核心框架和概念解析&#xff1a; 一、MyBatis 框架概述 1. 核心定位 作用&#xff1a;将 Java 对象…...

Vivado FPGA 开发 | 创建工程 / 仿真 / 烧录

注&#xff1a;本文为 “Vivado FPGA 开发 | 创建工程 / 仿真 / 烧录” 相关文章合辑。 略作重排&#xff0c;未整理去重。 如有内容异常&#xff0c;请看原文。 Vivado 开发流程&#xff08;手把手教学实例&#xff09;&#xff08;FPGA&#xff09; 不完美先生 于 2018-04-…...

PowerShell从5.1升级到7.X

文章目录 环境背景安装PowerShell 7.X其它启动PowerShell 5.1和7.X$PSVersionTable.PSVersion启动PowerShell 5.1时强制启动7.X 参考 环境 Windows 11 专业版 背景 PowerShell 5.1是Windows内置的&#xff0c;发布时间是2016 年。现在PowerShell版本已经到了7.5.1&#xff0…...

域名与官网的迷思:数字身份认证的全球困境与实践解方-优雅草卓伊凡

域名与官网的迷思&#xff1a;数字身份认证的全球困境与实践解方-优雅草卓伊凡 一、官网概念的法律与技术界定 1.1 官网的实质定义 当卓伊凡被问及”公司域名就是官网吗”这一问题时&#xff0c;他首先指出&#xff1a;”这相当于问’印着某公司logo的建筑就是该公司总部吗’…...

Vue实现成绩增删案例

Vue实现成绩增删案例 案例功能需求案例实现实现思路完整代码功能演示 案例小结 案例功能需求 1.通过vue渲染数据&#xff0c;将成绩的相关信息显示出来&#xff08;学号&#xff0c;学科&#xff0c;成绩&#xff09; 2.能够增加相关的成绩信息 3.能够删除相关的成绩信息 4.能…...

开源项目实战学习之YOLO11:ultralytics-cfg-models-rtdetr(十一)

&#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 &#x1f449; 点击关注不迷路 文章大纲 1. __init__.py2. model.py3. predict.py4. train.py5. val.py ultralytics-cfg-models-rtdetr 主要与 Ultralytics 库中 RTDETR&#xff08;实时目标检测模型&#xff0c;R…...

【Bootstrap V4系列】学习入门教程之 组件-按钮(Buttons)

Bootstrap V4系列 学习入门教程之 组件-按钮&#xff08;Buttons&#xff09; 按钮&#xff08;Buttons&#xff09;一、示例二、可用作按钮的 HTML 标签三、带轮廓线的按钮四、按钮的尺寸五、活动状态六、禁用状态七、按钮插件切换状态Checkbox and radio buttons &#xff08…...

【java八股文】深入浅出synchronized优化原理

&#x1f50d; 开发者资源导航 &#x1f50d;&#x1f3f7;️ 博客主页&#xff1a; 个人主页&#x1f4da; 专栏订阅&#xff1a; JavaEE全栈专栏 synchronized优化原理 synchronized即使悲观锁也是乐观锁&#xff0c;拥有自适应性。 jvm内部会统计每个锁的竞争激烈程度&…...

裴蜀定理及其证明

裴蜀定理 对于所有整数 a a a和 b b b&#xff0c;存在&#xff1a; g c d ( a , b ) a x b y gcd(a,b)axby gcd(a,b)axby 并且 a x b y axby axby一定是 g c d ( a , b ) gcd(a,b) gcd(a,b)的倍数。 证明 定义一个集合&#xff1a; { a x b y &#xff5c; a x b y &…...

单片机嵌入式CAN库

kw_can库说明 本库是针对CAN类型数据的收发设计&#xff1a; 主要应用于大数据量&#xff08;数据处理速度高于缓存CAN_RTX_FIFO_SIZE大小&#xff09;接收不丢包可快速进出接收中断可跨线程调用发送接口。 本库开源连接地址&#xff1a;gitee连接 实现思路 本库采用C语言…...

基于 JSP 和 Servlet 的数字信息分析小应用

Java Web 实验&#xff1a;基于 JSP 和 Servlet 的数字信息分析小应用 一、实验目的 实现一个简单的 Java Web 应用&#xff0c;通过 JSP 表单收集用户输入的文本信息&#xff0c;提交至 Servlet 分析其中是否包含数字&#xff0c;并返回结果。掌握 JSP 与 Servlet 的协同工作…...

从零认识阿里云OSS:云原生对象存储的核心价值

引言 在云计算时代&#xff0c;海量数据的存储与管理成为企业数字化转型的关键命题。阿里云对象存储OSS&#xff08;Object Storage Service&#xff09;作为云原生的分布式存储服务&#xff0c;凭借其独特的架构设计和丰富的功能矩阵&#xff0c;正在成为企业构建数据湖、管理…...

2025年深圳杯数学建模(东三省)B题【颜色转换】原论文讲解

大家好呀&#xff0c;从发布赛题一直到现在&#xff0c;总算完成了2025年深圳杯数学建模&#xff08;东三省&#xff09;B题【颜色转换】完整的成品论文。 给大家看一下目录吧&#xff1a; 目录 摘 要&#xff1a; 一、问题重述 二&#xff0e;问题分析 2.1问题一 2.2问…...

开源语音合成和转换项目

开源语音合成和转换项目 大模型出来以后&#xff0c;语音合成和转换方面也有了很大的变化。在语音转换文字方面有Whisper、SeamlessM4T等&#xff1b;在语音合成方面有ChatTTS&#xff08;中英文&#xff09;、Orpheus TTS&#xff08;仅仅支持英文&#xff09;、Amphion&…...

考研408《计算机组成原理》复习笔记,第二章计算机性能

一、计算机各项性能指标 1、计算机系统整体的性能指标&#xff1a; 从宏观上看&#xff0c;整个计算机是由软件硬件共同性能决定的&#xff0c;但是【最主要的决定性的影响】还是来自于【硬件】 因为计算机组成原理主要讲【硬件】&#xff0c;那么我们也仅考虑【硬件性能】 2…...

智能决策支持系统的基本概念与理论体系

决策支持系统是管理科学的一个分支&#xff0c;原本与人工智能属于不同的学科范畴&#xff0c;但自20世纪80年代以来&#xff0c;由于专家系统在许多方面取得了成功&#xff0c;于是人们开始考虑把人工智能技术用于计算机管理中来。在用计算机所进行的各种管理中&#xff0c;如…...

什么是运算符重载

运算符重载&#xff0c;就是对已有的运算符重新进行定义&#xff0c;赋予其另一种功能&#xff0c;以适应不同的数据类型&#xff0c;本质上是函数重载。以下为您详细介绍&#xff1a; 实现原理与方式 - 原理&#xff1a;把指定的运算表达式转化为对运算符函数的调用&#xff0…...