[实现Rpc] 环境搭建 | JsonCpp | Mudou库 | callBack()
目录
1. 项目介绍
2. 技术选型
3. 开发环境和环境搭建
Ubuntu-22.04环境搭建
1. 安装 wget(一般情况下默认会自带)
2. 更换国内软件源
① 备份原始 /etc/apt/sources.list 文件
② 编辑软件源文件
③ 更新软件包列表
3. 安装常用工具
3.1 安装 lrzsz 传输工具
3.2 安装编译器 gcc/g++
3.3 安装项目构建工具 make
3.4 安装调试器 gdb
3.5 安装版本控制系统 git
3.6 安装 cmake
3.7 安装 jsoncpp
4. 安装 Muduo 库
4. 第三方库使用介绍
4.1 JsonCpp库
4.1.1 Json的数据格式
4.1.2 JsonCpp介绍
4.1.3 JsonCpp使用
4.2 Muduo库
4.2.1 Muduo库是什么
4.2.2 Muduo库常见接口介绍
1. TcpServer 类
2. EventLoop 类
3. TcpConnection 类
4. TcpClient 类
5. Buffer 类
4.2.3 Muduo库的使用
1. 实现英译汉 TCP 服务器
2. 实现英译汉 TCP 客户端
3. Makefile 编译指令
运行结果
回调函数
1. 解耦网络事件处理逻辑
2. 事件驱动的核心机制
3. 提高代码复用性与可维护性
4. 非阻塞与多线程安全
5. 动态响应不同需求
1. 项目介绍
(1)
- RPC(Remote Procedure Call)远程过程调用,是⼀种通过网络从远程计算机上请求服务,而不需要了解底层网络通信细节。
- RPC可以使用多种网络协议进行通信, 如HTTP、TCP、UDP等, 并且在TCP/IP网络四层模型中跨越了传输层和应用层。
- 简言之 RPC就是像调用本地方法⼀样调用远程方法。
此过程可以理解为业务处理、计算任务,更直白的说,就是程序/方法/函数等,就是像调用本地放法⼀样调用远程放法。
(2)
举个形象点谈恋爱的例子:
- 本地过程调用:恋爱对象在你的身边, 可以随时约对象吃饭、看电影、约会等等
- 远端过程调用:好像异地恋⼀样, 隔着千山万水, 如果想约会, 需要先和对象进行约定,在坐火车/飞机赶到约定的地点
(3)
⼀个完整RPC通信框架,大概包含以下内容:
- 序列化协议。
- 通信协议。
- 连接复用。
- 服务注册。
- 服务发现。
- 服务订阅和通知。
- 负载均衡。
- 服务监控。
- 同步调用。
- 异步调用。
本项目是基于C++、JsonCpp、muduo网络库实现⼀个简单、易用的RPC通信框架
实现了 同步调用、异步callback调用、异步futrue调用、服务注册/发现/上线/下线,主题发布订阅等功能设计。
2. 技术选型
(1)目前RPC的实现方案有两种:
第一种是 client和server继承公共接口:
- 根据IDL(接口描述语言)定义公共接口。
- 编写代码生成器根据IDL语言生成相关的C++、Java代码
- 然后我们的客户端和服务器程序 共同向上继承公共接口即可
比如我们常用的Protobuf、json可以定义IDL接口,并生成RPC相关的代码
- 缺点:使用pb因为生成⼀部分代码, 所以对理解不够友好
- 如果是json定义IDL语言需要自己编写代码⽣成器难度较大一点, 暂不考虑这种方案
第二种实现⼀个远程调用接口call,然后通过传入函数名参数来调用RPC接口。本项目采用这种实现方案。
(2)网络传输的参数和返回值 怎么映射到对应的RPC 接口上?
- 使用protobuf的反射机制。
- 使用C++模板、类型萃取、函数萃取等机制。
- 使用更通用的类型, 比如 JSON类型, 设计好参数和返回值协议即可。
前两种技术难度和学习成本较高, 本项目使用第三种方式。
(3)网络传输怎么做?
- 原生socket - 实现难度较⼤, 暂不考虑。
- Boost asio库的异步通信 - 需要扩展boost库。
- 使用muduo库,学习开发成本较低。
(4)序列化和反序列化?
- Protobuf:可选。
- JSON:因为项⽬需要使⽤JSON来定义函数参数和返回值,所以本项目中 直接采用JSON进行序列化和反序列化。
3. 开发环境和环境搭建
- Linux(Centos-7.6 / Ubuntu-22.04)。
- VSCode/Vim。
- g++/gdb。
- Makefile。
Ubuntu-22.04环境搭建
1. 安装 wget(一般情况下默认会自带)
在Ubuntu 22.04中,wget
通常已经预安装。如果需要确保它已安装,可以运行以下命令:
sudo apt-get update
sudo apt-get install -y wget
2. 更换国内软件源
为了加速下载速度和提高稳定性,可以将官方的APT源更换为国内镜像源。
① 备份原始 /etc/apt/sources.list
文件
首先备份原来的软件源文件,以防出现问题时可以恢复:
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
② 编辑软件源文件
接下来,使用文本编辑器打开并编辑/etc/apt/sources.list
文件以添加新的源。推荐使用nano
或您喜欢的其他编辑器:
sudo nano /etc/apt/sources.list
然后,在文件中添加或替换以下内容(这里使用了阿里云和清华大学的镜像源):
deb http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-security main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-updates main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-proposed main restricted universe multiverse
deb http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse
deb-src http://mirrors.aliyun.com/ubuntu/ focal-backports main restricted universe multiverse# 添加清华源
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-updates main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-backports main restricted universe multiverse
deb https://mirrors.tuna.tsinghua.edu.cn/ubuntu/ focal-security main restricted universe multiverse
请注意,这里的focal
是Ubuntu 20.04 LTS的代号。对于Ubuntu 22.04,请根据实际情况更改为jammy
。
③ 更新软件包列表
保存更改后,通过以下命令更新APT软件包列表:
sudo apt-get update
3. 安装常用工具
现在可以安装一些常用的开发工具和库。
3.1 安装 lrzsz 传输工具
用于从远程服务器上传和下载文件:
sudo apt-get install lrzsz
3.2 安装编译器 gcc/g++
安装GNU编译器集合,包括C和C++编译器:
sudo apt-get install gcc g++
3.3 安装项目构建工具 make
用于管理和构建项目:
sudo apt-get install make
3.4 安装调试器 gdb
用于调试程序:
sudo apt-get install gdb
3.5 安装版本控制系统 git
用于代码版本管理:
sudo apt-get install git
3.6 安装 cmake
用于跨平台的项目构建系统:
sudo apt-get install cmake
3.7 安装 jsoncpp
用于解析JSON数据格式:
sudo apt-get install libjsoncpp-dev
4. 安装 Muduo 库
1. 下载源码
可以通过 git
命令从 GitHub 克隆 Muduo 的源码到本地:
# 使用 git 克隆源码
git clone https://github.com/chenshuo/muduo.git
2. 安装依赖环境
Muduo 依赖于以下库,需要通过包管理工具安装相关依赖:
# 安装 zlib 和 Boost 库
sudo apt-get install libz-dev libboost-all-dev
3. 编译与安装
执行以下步骤完成编译和安装:
- 解压源码(如适用):
如果下载的是压缩包(如muduo-master.zip
),需要先解压。 -
unzip muduo-master.zip cd muduo-master
- 运行编译脚本:
使用build.sh
脚本编译 Muduo 库。 -
./build.sh
- 安装到系统目录:
运行安装脚本将编译生成的库文件安装到系统路径。
./build.sh install
安装成功:
4. 第三方库使用介绍
4.1 JsonCpp库
4.1.1 Json的数据格式
Json简介
Json(JavaScript Object Notation)是一种轻量级的数据交换格式
- 它采用完全独立于编程语言的文本格式来存储和表示数据。
- 这种格式易于人阅读和编写,同时也易于机器解析和生成。
示例:学生信息表示
- C/C++代码表示:
char *name = "xx";
int age = 18;
float score[3] = {88.5, 99, 58};
- Json 表示:
{"姓名" : "xx","年龄" : 18,"成绩" : [88.5, 99, 58],"爱好" :{"书籍" : "西游记","运动" : "打篮球"}
}
Json 的数据类型
- 对象:使用花括号
{}
括起来表示一个对象。 - 数组:使用中括号
[]
括起来表示一个数组。 - 字符串:使用常规双引号
""
括起来表示一个字符串。 - 数字:包括整形和浮点型,直接使用。
4.1.2 JsonCpp介绍
Jsoncpp库概述
Jsoncpp库主要用于实现Json格式数据的序列化和反序列化,即它可以将多个数据对象组织成Json格式字符串,并且可以从Json格式字符串解析得到多个数据对象。
⭕Json 数据对象类 Value
class Json::Value
{Value &operator=(const Value &other); // Value重载了[]和=,因此所有的赋值和获取数据都可以通过简单的方式完成Value& operator[](const std::string& key); // val["name"] = "xx";Value& operator[](const char* key);Value removeMember(const char* key); // 移除元素const Value& operator[](ArrayIndex index) const; // val["score"][0]Value& append(const Value& value); // 添加数组元素 val["score"].append(88);ArrayIndex size() const; // 获取数组元素个数 val["score"].size();std::string asString() const; // 转换为 string string name = val["name"].asString();const char* asCString() const; // 转换为 char* char *name = val["name"].asCString();Int asInt() const; // 转换为 int int age = val["age"].asInt();float asFloat() const; // 转换为 float float weight = val["weight"].asFloat();bool asBool() const; // 转换为 bool bool ok = val["ok"].asBool();
};
序列化接口
class JSON_API StreamWriter
{virtual int write(Value const& root, std::ostream* sout) = 0;
};class JSON_API StreamWriterBuilder : public StreamWriter::Factory
{virtual StreamWriter* newStreamWriter() const;
};
反序列化接口
class JSON_API CharReader
{virtual bool parse(char const* beginDoc, char const* endDoc, Value* root, std::string* errs) = 0;
};class JSON_API CharReaderBuilder : public CharReader::Factory
{virtual CharReader* newCharReader() const;
};
序列化和反序列化都是需要通过工厂类来进行实例化。
4.1.3 JsonCpp使用
代码编写示例
下面是一个简单的例子,展示了如何使用Jsoncpp库进行序列化和反序列化操作。
#include <iostream>
#include <string>
#include <jsoncpp/json/json.h>
#include <sstream>
#include <memory>// 序列化函数
bool serialize(const Json::Value &val, std::string &body)
{std::stringstream ss;Json::StreamWriterBuilder swb;std::unique_ptr<Json::StreamWriter> w(swb.newStreamWriter());bool ret = w->write(val, &ss);if (!ret) return false;body = ss.str();return true;
}// 反序列化函数
bool deserialize(const std::string &body, Json::Value &val)
{Json::CharReaderBuilder crb;std::unique_ptr<Json::CharReader> r(crb.newCharReader());std::string errs;bool ret = r->parse(body.c_str(), body.c_str() + body.size(), &val, &errs);if (!ret) {std::cout << "json unserialize failed : " << errs << std::endl;return false;}return true;
}int main()
{const char* sex = "男";const char* name = "小明";int age = 18;int score[4] = {70, 80, 90};Json::Value stu1;stu1["姓名"] = name;stu1["性别"] = sex;stu1["年龄"] = age;for (auto s : score) {stu1["分数"].append(s);}Json::Value fav;fav["书籍"] = "西游记";fav["运动"] = "打篮球";stu1["爱好"] = fav;std::string body;bool ret1 = serialize(stu1, body);std::cout << body << std::endl;std::string str = R"({"姓名":"小黑", "年龄": 19, "成绩":[32, 45, 56]})";Json::Value stu2;bool ret2 = deserialize(str, stu2);if (!ret2) return -1;std::cout << "姓名: " << stu2["姓名"].asString() << std::endl;std::cout << "年龄: " << stu2["年龄"].asInt() << std::endl;for (int i = 0; i < static_cast<int>(stu2["成绩"].size()); ++i) {std::cout << "成绩: " << stu2["成绩"][i].asFloat() << std::endl;}return 0;
}
⭕注意:对于头文件的包含调用
运行:
4.2 Muduo库
4.2.1 Muduo库是什么
Muduo是由陈硕老师开发的 基于非阻塞IO和事件驱动的C++高并发TCP网络编程库。它采用 主从Reactor模型 和 one loop per thread 线程模型。
⭕one loop per thread 的含义:
- 一个线程只能有一个事件循环(
EventLoop
),用于响应计时器和IO事件。- 一个 文件描述符只能由一个线程进行读写,即一个TCP连接必须归属于某个
EventLoop
管理。
从而避免了,例如一万个请求就创建一万个线程的等待浪费,主从的结构也将 对于网络连接的接收和对于信息的处理 进行了拆分。
4.2.2 Muduo库常见接口介绍
1. TcpServer 类
TcpServer
用于服务端管理和维护 TCP 连接。核心接口包括:
- 构造函数:
TcpServer(EventLoop* loop, const InetAddress& listenAddr, const string& nameArg, Option option = kNoReusePort);
-
- 参数:
-
-
loop
:主EventLoop
,用于事件循环。listenAddr
:服务器监听地址。nameArg
:服务器实例名称。option
:是否复用端口。
-
- 线程设置与启动:
void setThreadNum(int numThreads); // 设置IO线程数量
void start(); // 启动服务端监听
- 回调函数设置:
void setConnectionCallback(const ConnectionCallback& cb); // 连接建立/断开回调
void setMessageCallback(const MessageCallback& cb); // 消息接收回调
2. EventLoop 类
EventLoop
是事件循环的核心类。
- 循环控制:
void loop(); // 启动事件循环
void quit(); // 退出事件循环
- 定时器接口:
TimerId runAt(Timestamp time, TimerCallback cb); // 在指定时间运行回调
TimerId runAfter(double delay, TimerCallback cb); // 指定延迟后运行回调
TimerId runEvery(double interval, TimerCallback cb); // 周期性运行回调
void cancel(TimerId timerId); // 取消定时器
3. TcpConnection 类
TcpConnection
负责维护单个TCP连接。
- 核心功能:
void send(const void* message, int len); // 发送数据
void shutdown(); // 关闭连接
- 上下文管理:
void setContext(const boost::any& context); // 设置上下文
const boost::any& getContext() const; // 获取上下文
- 回调函数:
void setConnectionCallback(const ConnectionCallback& cb); // 设置连接回调
void setMessageCallback(const MessageCallback& cb); // 设置消息回调
4. TcpClient 类
TcpClient
用于客户端管理 TCP 连接。
- 核心方法:
void connect(); // 发起连接
void disconnect(); // 断开连接
void stop(); // 停止客户端
- 回调函数:
void setConnectionCallback(ConnectionCallback cb); // 设置连接回调
void setMessageCallback(MessageCallback cb); // 设置消息回调
- 注意事项:
由于TcpClient
操作是异步的,在连接建立之前尝试发送数据是不被允许的,可通过CountDownLatch
进行同步。
5. Buffer 类
Buffer
用于管理数据缓冲区。
- 核心方法:
size_t readableBytes() const; // 可读数据大小
size_t writableBytes() const; // 可写数据大小
string retrieveAllAsString(); // 取出所有数据并转为字符串
void append(const char* data, size_t len); // 添加数据
void prepend(const void* data, size_t len); // 在缓冲区头部追加数据
- 内部实现:
使用std::vector<char>
作为缓冲区,支持动态扩容。
4.2.3 Muduo库的使用
1. 实现英译汉 TCP 服务器
代码示例:
- 核心逻辑:
-
- 使用
TcpServer
管理服务器。 - 注册
onConnection
和onMessage
回调函数。 - 在
onMessage
中,通过字典查找翻译单词,并返回给客户端。
- 使用
代码实现:
class DictServer {
public:DictServer(int port):_server(&_baseloop, InetAddress("0.0.0.0", port), "DictServer", TcpServer::kReusePort){_server.setConnectionCallback(std::bind(&DictServer::onConnection, this, std::placeholders::_1));_server.setMessageCallback(std::bind(&DictServer::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));}void Start() {_server.start();_baseloop.loop();}private:void onConnection(const TcpConnectionPtr &conn) {if (conn->connected()) std::cout << "连接建立!\n";else std::cout << "连接断开!\n";}void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp) {std::unordered_map<std::string, std::string> dict = {{"hello", "你好"}, {"world", "世界"}};std::string msg = buf->retrieveAllAsString();conn->send(dict.count(msg) ? dict[msg] : "未知单词!");}private:EventLoop _baseloop;TcpServer _server;
};int main() {DictServer server(8080);server.Start();return 0;
}
2. 实现英译汉 TCP 客户端
代码示例:
- 核心逻辑:
-
- 使用
TcpClient
连接服务器。 - 注册回调函数
onConnection
和onMessage
。 - 在建立连接后,通过
send
方法发送单词。
- 使用
代码实现:
class DictClient {
public:DictClient(const std::string &ip, int port):_baseloop(_loopthread.startLoop()), _latch(1), _client(_baseloop, InetAddress(ip, port), "DictClient"){_client.setConnectionCallback(std::bind(&DictClient::onConnection, this, std::placeholders::_1));_client.setMessageCallback(std::bind(&DictClient::onMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.connect();_latch.wait();}void send(const std::string &msg) {if (_conn->connected()) _conn->send(msg);else std::cout << "连接断开,发送失败!\n";}private:void onConnection(const TcpConnectionPtr &conn) {if (conn->connected()) {_conn = conn;_latch.countDown();} else {_conn.reset();}}void onMessage(const TcpConnectionPtr &conn, Buffer *buf, Timestamp) {std::cout << buf->retrieveAllAsString() << std::endl;}private:EventLoop *_baseloop;EventLoopThread _loopthread;TcpClient _client;TcpConnectionPtr _conn;CountDownLatch _latch;
};int main() {DictClient client("127.0.0.1", 8080);std::string msg;while (std::cin >> msg) client.send(msg);return 0;
}
3. Makefile 编译指令
CFLAG= -std=c++11 -I ../build/release-install-cpp11/include/
LFLAG= -L../build/release-install-cpp11/lib -lmuduo_net -lmuduo_base -pthreadall: server clientserver: server.cppg++ $(CFLAG) $^ -o $@ $(LFLAG)client: client.cppg++ $(CFLAG) $^ -o $@ $(LFLAG)clean:rm -f server client
运行结果
通过简单输入单词实现实时翻译,服务器返回翻译结果或提示“未知单词”。
客户端
回调函数
在 Muduo 中使用回调函数(如 setConnectionCallback
和 setMessageCallback
)是为了实现网络库的 事件驱动编程模型,这符合非阻塞 I/O 和高并发网络编程的设计理念。
🔷 为什么要借助回调函数呢:
1. 解耦网络事件处理逻辑
Muduo 库本身只负责处理网络事件的底层逻辑,比如连接建立、断开、消息接收等,但具体的业务逻辑是用户的需求(如数据如何处理、何时响应客户端等),而这些需求是 多变且不可预测的。
- 问题:如果将业务逻辑直接嵌入到 Muduo 内部,不仅会导致耦合度过高,还会限制灵活性。
- 解决:通过 回调函数,用户可以将自己的业务逻辑注册给 Muduo,在事件发生时 Muduo 自动调用注册的回调函数。
示例:
- 连接建立或断开时,用户可以通过
setConnectionCallback
注册逻辑来处理事件(如记录日志、初始化资源等)。 - 消息到达时,通过
setMessageCallback
注册回调处理消息(如解析请求、响应客户端)。
2. 事件驱动的核心机制
Muduo 的设计基于 事件驱动模型,回调函数是这种模型的核心:
- 在事件循环(
EventLoop
)中,Muduo 会监听文件描述符(如 socket)上的事件。 - 当检测到事件(如新连接、可读数据等),会触发相应的回调函数。
使用回调函数的好处:
- 异步非阻塞:Muduo 不会阻塞等待事件完成,而是注册回调函数后,立即返回继续处理其他事件。
- 灵活性高:用户可以根据业务需要,提供任意逻辑来响应事件,而无需修改框架本身。
示例:
void onConnection(const TcpConnectionPtr& conn) {if (conn->connected()) {std::cout << "新连接建立:" << conn->peerAddress().toIpPort() << std::endl;} else {std::cout << "连接断开" << std::endl;}
}
用户通过 setConnectionCallback
将 onConnection
注册给 TcpServer
,当连接建立或断开时,TcpServer
自动调用用户定义的逻辑。
3. 提高代码复用性与可维护性
回调函数机制使得:
- 复用性高:Muduo 的底层逻辑和用户的业务逻辑是解耦的,可以分别复用。例如,Muduo 可用于不同类型的服务器(如 HTTP 服务器、聊天服务器),而无需修改核心代码。
- 易于维护:用户只需关心自己的回调函数,不需要深入理解网络库内部的实现。框架和业务逻辑的分离使得代码更清晰易维护。
4. 非阻塞与多线程安全
在高并发场景中,回调函数的设计可以帮助 Muduo:
- 避免阻塞:通过异步回调实现逻辑处理,避免阻塞事件循环线程(
EventLoop
)。 - 线程安全:Muduo 使用
one loop per thread
模型,每个线程一个EventLoop
,回调函数在绑定的线程中执行,不需要担心多线程竞态问题。
5. 动态响应不同需求
回调函数的动态绑定特性允许用户在运行时根据需求改变逻辑:
- 用户可以在程序启动时或运行时,调用
setConnectionCallback
或setMessageCallback
设置不同的回调函数,动态调整行为。
示例:
// 设置新的消息回调函数
server.setMessageCallback([](const TcpConnectionPtr& conn, Buffer* buf, Timestamp) {std::string msg = buf->retrieveAllAsString();conn->send("Echo: " + msg);
});
通过新的回调函数,程序逻辑可以轻松从英译汉服务器切换为回声服务器,而无需修改其他部分的代码。
总结
使用回调函数的原因总结如下:
- 解耦网络库与业务逻辑,增强灵活性。
- 事件驱动模型的核心机制,支持异步非阻塞操作。
- 提高复用性与可维护性,框架和业务逻辑分离。
- 适应高并发需求,避免阻塞并提供线程安全保证。
- 支持动态逻辑调整,满足运行时多样化需求。
回调函数是事件驱动编程中不可或缺的设计模式,Muduo 通过它实现了高性能、高并发的网络框架设计理念。
相关文章:

[实现Rpc] 环境搭建 | JsonCpp | Mudou库 | callBack()
目录 1. 项目介绍 2. 技术选型 3. 开发环境和环境搭建 Ubuntu-22.04环境搭建 1. 安装 wget(一般情况下默认会自带) 2. 更换国内软件源 ① 备份原始 /etc/apt/sources.list 文件 ② 编辑软件源文件 ③ 更新软件包列表 3. 安装常用工具 3.1 安装…...
llamafactory使用8张昇腾910b算力卡lora微调训练qwen2-72b大模型
说明 我需要在昇腾服务器上对Qwen2-72B大模型进行lora微调,改变其自我认知。 我的环境下是8张910B1卡。显存约512GB。 准备:安装llamafactory 请参考官方方法安装llamafactory:https://github.com/hiyouga/LLaMA-Factory 特别强调下&…...

C++,设计模式,【目录篇】
文章目录 1. 简介2. 设计模式的分类2.1 创建型模式(Creational Patterns):2.2 结构型模式(Structural Patterns):2.3 行为型模式(Behavioral Patterns): 3. 使用设计模式…...

《目标检测数据集下载地址》
一、引言 在计算机视觉的广袤领域中,目标检测宛如一颗璀璨的明星,占据着举足轻重的地位。它宛如赋予计算机一双锐利的 “眼睛”,使其能够精准识别图像或视频中的各类目标,并确定其位置,以边界框的形式清晰呈现。这项技…...
C 语言的void*到底是什么?
一、void* 的类型任意性 void* 是一种通用指针类型。它可以指向任意类型的数据。例如,它可以指向一个整数(int)、一个浮点数(float)、一个字符(char)或者一个结构体等。在C语言中,当…...
Linux中的文件上传和下载
Linux中的文件上传和下载 一、连接 SFTP 在 SecureCRT 中,将鼠标移动到连接窗口的标题上,按鼠标右键,选择“连接 SFTP”标签,即可进入 SFTP 模式。 二、基本指令及用途 1. 显示当前目录 显示本地当前目录:lpwd 示例…...

DDD - 微服务落地的技术实践
文章目录 Pre概述如何发挥微服务的优势怎样提供微服务接口原则微服务的拆分与防腐层的设计 去中心化的数据管理数据关联查询的难题Case 1Case 2Case 3 总结 Pre DDD - 软件退化原因及案例分析 DDD - 如何运用 DDD 进行软件设计 DDD - 如何运用 DDD 进行数据库设计 DDD - 服…...
fgets、scanf存字符串应用
题目1 夺旗(英语:Capture the flag,简称 CTF)在计算机安全中是一种活动,当中会将“旗子”秘密地埋藏于有目的的易受攻击的程序或网站。参赛者从其他参赛者或主办方偷去旗子。 非常崇拜探姬的小学妹最近迷上了 CTF&am…...

鸿蒙动态路由实现方案
背景 随着CSDN 鸿蒙APP 业务功能的增加,以及为了与iOS、Android 端统一页面跳转路由,以及动态下发路由链接,路由重定向等功能。鸿蒙动态路由方案的实现迫在眉睫。 实现方案 鸿蒙版本动态路由的实现原理,类似于 iOS与Android的实…...

Spring-boot3.4最新版整合swagger和Mybatis-plus
好家伙,今天终于开始用spring-boot3开始写项目了,以后要彻底告别1.x和2.x了,同样的jdk也来到了最低17的要求了,废话不多说直接开始 这是官方文档的要求jdk最低是17 maven最低是3.6 一. 构建工程,这一步就不需要给大家解释了吧 二. 整合Knife4j 1.大于…...

基于Java的高校实习管理平台
基于Java的高校实习管理平台是一个专为高校设计的信息化管理工具,旨在通过信息化手段简化实习管理流程,提高管理效率,增强学校、企业与学生之间的沟通与协作。: 一、系统背景与意义 随着教育体系的不断完善和就业市场的日益竞争…...
全国青少年信息学奥林匹克竞赛(信奥赛)备考实战之一维数组(应用技巧)
二、一维数组应用技巧2:打标记 实战训练1—开关灯 问题描述: 有 M个从1到M依次编号的人参加一项游戏。将K 盏从1到K依次编号的灯(K和M均为正整数,M≤K≤5000)进行一系列的熄灭与打开的操作,游戏开始时均处于亮灯的状态…...

【2024年华为OD机试】 (B卷,100分)- 路灯照明问题(Java JS PythonC/C++)
一、问题描述 路灯照明问题 题目描述 在一条笔直的公路上安装了 ( N ) 个路灯,从位置 0 开始安装,路灯之间间距固定为 100 米。每个路灯都有自己的照明半径。请计算第一个路灯和最后一个路灯之间,无法照明的区间的长度和。 输入描述 第一…...
SVGAPlayer error 处理
提示错误 Call to undeclared function OSAtomicCompareAndSwapPtrBarrier; ISO C99 and later do not support implicit function declarations Conflicting types for OSAtomicCompareAndSwapPtrBarrier Declaration of OSAtomicCompareAndSwapPtrBarrier must be imported …...
2024年12月电子学会青少年机器人技术等级考试(二级)实际操作试卷
2024.12 青少年机器人技术等级考试(二级)实际操作试卷 一、多选题 第 1 题 关于后轮驱动车说法正确的有哪些?( ) A.起步加速表现比前轮驱动好 B.容易转向过度 C.车身重量比前轮驱动更均衡 D.造价比前轮驱动车更高…...
Swift 专题二 语法速查
一 、变量 let, var 变量是可变的,使用 var 修饰,常量是不可变的,使用 let 修饰。类、结构体和枚举里的变量是属性。 var v1:String "hi" // 标注类型 var v2 "类型推导" let l1 "标题" // 常量class a {…...

Api网关Zuul
网关分类与开放API 开放API (OpenAPI) 企业需要将自身数据、能力等作为开发平台向外开放,通常会以REST的方式向外提供,最好的例子就是淘宝开放平台、腾讯公司的QQ开发平台、微信开放平台。开放API平台必然涉及到客户应用的接入、API权限的管理、调用次数…...

01设计模式(D3_设计模式类型 - D3_行为型模式)
目录 一、模版方法模式 1. 基本介绍 2. 应用案例一:豆浆制作问题 需求 代码实现 模板方法模式的钩子方法 3. View的draw(Android) Android中View的draw方法就是使用了模板方法模式 模板方法模式在 Spring 框架应用的源码分析 知识小…...

python编程-OpenCV(图像读写-图像处理-图像滤波-角点检测-边缘检测)角点检测
角点检测(Corner Detection)是计算机视觉和图像处理中重要的步骤,主要用于提取图像中的关键特征,以便进行后续的任务,比如图像匹配、物体识别、运动跟踪等。下面介绍几种常用的角点检测方法及其应用。 1. Harris角点检…...

费解的开关
费解的开关 你玩过“拉灯”游戏吗? 25 盏灯排成一个 55 的方形。 每一个灯都有一个开关,游戏者可以改变它的状态。 每一步,游戏者可以改变某一个灯的状态。 游戏者改变一个灯的状态会产生连锁反应:和这个灯上下左右相邻的灯也…...

XML Group端口详解
在XML数据映射过程中,经常需要对数据进行分组聚合操作。例如,当处理包含多个物料明细的XML文件时,可能需要将相同物料号的明细归为一组,或对相同物料号的数量进行求和计算。传统实现方式通常需要编写脚本代码,增加了开…...
Android Wi-Fi 连接失败日志分析
1. Android wifi 关键日志总结 (1) Wi-Fi 断开 (CTRL-EVENT-DISCONNECTED reason3) 日志相关部分: 06-05 10:48:40.987 943 943 I wpa_supplicant: wlan0: CTRL-EVENT-DISCONNECTED bssid44:9b:c1:57:a8:90 reason3 locally_generated1解析: CTR…...

智能在线客服平台:数字化时代企业连接用户的 AI 中枢
随着互联网技术的飞速发展,消费者期望能够随时随地与企业进行交流。在线客服平台作为连接企业与客户的重要桥梁,不仅优化了客户体验,还提升了企业的服务效率和市场竞争力。本文将探讨在线客服平台的重要性、技术进展、实际应用,并…...
python爬虫:Newspaper3k 的详细使用(好用的新闻网站文章抓取和解析的Python库)
更多内容请见: 爬虫和逆向教程-专栏介绍和目录 文章目录 一、Newspaper3k 概述1.1 Newspaper3k 介绍1.2 主要功能1.3 典型应用场景1.4 安装二、基本用法2.2 提取单篇文章的内容2.2 处理多篇文档三、高级选项3.1 自定义配置3.2 分析文章情感四、实战案例4.1 构建新闻摘要聚合器…...
数据库分批入库
今天在工作中,遇到一个问题,就是分批查询的时候,由于批次过大导致出现了一些问题,一下是问题描述和解决方案: 示例: // 假设已有数据列表 dataList 和 PreparedStatement pstmt int batchSize 1000; // …...

用docker来安装部署freeswitch记录
今天刚才测试一个callcenter的项目,所以尝试安装freeswitch 1、使用轩辕镜像 - 中国开发者首选的专业 Docker 镜像加速服务平台 编辑下面/etc/docker/daemon.json文件为 {"registry-mirrors": ["https://docker.xuanyuan.me"] }同时可以进入轩…...
Spring AI与Spring Modulith核心技术解析
Spring AI核心架构解析 Spring AI(https://spring.io/projects/spring-ai)作为Spring生态中的AI集成框架,其核心设计理念是通过模块化架构降低AI应用的开发复杂度。与Python生态中的LangChain/LlamaIndex等工具类似,但特别为多语…...

GC1808高性能24位立体声音频ADC芯片解析
1. 芯片概述 GC1808是一款24位立体声音频模数转换器(ADC),支持8kHz~96kHz采样率,集成Δ-Σ调制器、数字抗混叠滤波器和高通滤波器,适用于高保真音频采集场景。 2. 核心特性 高精度:24位分辨率,…...

网站指纹识别
网站指纹识别 网站的最基本组成:服务器(操作系统)、中间件(web容器)、脚本语言、数据厍 为什么要了解这些?举个例子:发现了一个文件读取漏洞,我们需要读/etc/passwd,如…...
【无标题】路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论
路径问题的革命性重构:基于二维拓扑收缩色动力学模型的零点隧穿理论 一、传统路径模型的根本缺陷 在经典正方形路径问题中(图1): mermaid graph LR A((A)) --- B((B)) B --- C((C)) C --- D((D)) D --- A A -.- C[无直接路径] B -…...