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

仿RabbitMq简易消息队列基础篇(Muduo库的使用)

@TOC

Muduo库简介

Muduo由陈硕⼤佬开发,是⼀个基于⾮阻塞IO和事件驱动的C++⾼并发TCP⽹络编程库。他是一款基于主从Reactor模型的网络库,其使用的线程模型是one loop per thread, 所谓 one loop per thread 指的是:

  • 一个线程只能有一个事件循环(EventLoop),用于相应计时器和IO时间
  • 一个文件描述符只能由一个线程进行读写,换句话说就是一个TCP连接必须归属某个EventLoop管理

Muduo库常见接口介绍

muduo::net::TcpServer类基础介绍

typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::function<void(const TcpConnectionPtr &)> ConnectionCallback;
typedef std::function<void(const TcpConnectionPtr &,Buffer *,Timestamp)>MessageCallback;
class InetAddress : public muduo::copyable
{
public:InetAddress(StringArg ip, uint16_t port, bool ipv6 = false);
};
class TcpServer : noncopyable
{
public:enum Option{kNoReusePort,kReusePort,};TcpServer(EventLoop *loop,const InetAddress &listenAddr,const string &nameArg,Option option = kNoReusePort);void setThreadNum(int numThreads);void start();/// 当⼀个新连接建⽴成功的时候被调⽤void setConnectionCallback(const ConnectionCallback &cb){connectionCallback_ = cb;}/// 消息的业务处理回调函数---这是收到新连接消息的时候被调⽤的函数void setMessageCallback(const MessageCallback &cb){messageCallback_ = cb;}
};

muduo::net::EventLoop 类基础介绍

class EventLoop : noncopyable
{
public:/// 无限循环。/// 必须在与对象创建相同的线程中调用。void loop();/// 退出循环。/// 如果通过原始指针调用,这不是 100% 线程安全,/// 最好通过 shared_ptr<EventLoop> 调用以保证 100% 安全。void quit();TimerId runAt(Timestamp time, TimerCallback cb);/// 在 @c delay 秒后运行回调。/// 线程安全,可以从其他线程调用。TimerId runAfter(double delay, TimerCallback cb);/// 每隔 @c interval 秒运行一次回调。/// 线程安全,可以从其他线程调用。TimerId runEvery(double interval, TimerCallback cb);/// 取消定时器。/// 线程安全,可以从其他线程调用。void cancel(TimerId timerId);private:std::atomic<bool> quit_;std::unique_ptr<Poller> poller_;mutable MutexLock mutex_;std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
};

muduo::net::TcpConnection 类基础介绍

class TcpConnection : noncopyable,public std::enable_shared_from_this<TcpConnection>
{
public:/// 使用已连接的 sockfd 构造 TcpConnection////// 用户不应创建此对象。TcpConnection(EventLoop *loop,const string &name,int sockfd,const InetAddress &localAddr,const InetAddress &peerAddr);/// 检查连接是否已建立bool connected() const { return state_ == kConnected; }/// 检查连接是否已断开bool disconnected() const { return state_ == kDisconnected; }/// 发送消息(右值引用,C++11)void send(string &&message); // C++11/// 发送消息(指针和长度)void send(const void *message, int len);/// 发送消息(StringPiece)void send(const StringPiece &message);/// 发送消息(Buffer 指针,交换数据)void send(Buffer *message); // this one will swap data/// 关闭连接(非线程安全,不能同时调用)void shutdown(); // NOT thread safe, no simultaneous calling/// 设置上下文void setContext(const boost::any &context){context_ = context;}/// 获取上下文(常量引用)const boost::any &getContext() const{return context_;}/// 获取可变的上下文指针boost::any *getMutableContext(){return &context_;}/// 设置连接回调函数void setConnectionCallback(const ConnectionCallback &cb){connectionCallback_ = cb;}/// 设置消息回调函数void setMessageCallback(const MessageCallback &cb){messageCallback_ = cb;}private:/// 连接状态枚举enum StateE{kDisconnected,kConnecting,kConnected,kDisconnecting};EventLoop *loop_;                             // 事件循环指针ConnectionCallback connectionCallback_;       // 连接回调函数MessageCallback messageCallback_;             // 消息回调函数WriteCompleteCallback writeCompleteCallback_; // 写完成回调函数boost::any context_;                          // 上下文数据
};

muduo::net::TcpClient类基础介绍

class TcpClient : noncopyable
{
public:/// 构造函数,通过服务器地址和客户端名称构造TcpClientTcpClient(EventLoop *loop,const InetAddress &serverAddr,const string &nameArg);/// 析构函数,强制外部析构,以便管理std::unique_ptr成员~TcpClient(); // force out-line dtor, for std::unique_ptr members./// 连接服务器void connect();/// 断开连接void disconnect();/// 停止客户端void stop();/// 获取客户端对应的通信连接Connection对象的接口/// 在调用connect后,有可能连接还没有建立成功TcpConnectionPtr connection() const{MutexLockGuard lock(mutex_);return connection_;}/// 设置连接服务器成功时的回调函数void setConnectionCallback(ConnectionCallback cb){connectionCallback_ = std::move(cb);}/// 设置收到服务器发送的消息时的回调函数void setMessageCallback(MessageCallback cb){messageCallback_ = std::move(cb);}private:EventLoop *loop_;                                // 事件循环指针ConnectionCallback connectionCallback_;          // 连接回调函数MessageCallback messageCallback_;                // 消息回调函数WriteCompleteCallback writeCompleteCallback_;    // 写完成回调函数TcpConnectionPtr connection_ GUARDED_BY(mutex_); // 连接对象,受mutex_保护
};/*
需要注意的是,因为muduo库不管是服务器端还是客户端都是异步操作,
对于客户端来说,如果我们在连接还没有完全建立成功的时候发送数据,这是不被允许的。
因此我们可以使用内置的CountDownLatch类进行同步控制。
*/
class CountDownLatch : noncopyable
{
public:/// 显式构造函数,初始化倒计时计数explicit CountDownLatch(int count);/// 等待倒计时完成void wait(){MutexLockGuard lock(mutex_);while (count_ > 0){condition_.wait();}}/// 倒计时减一void countDown(){MutexLockGuard lock(mutex_);--count_;if (count_ == 0){condition_.notifyAll();}}/// 获取当前倒计时计数int getCount() const;private:mutable MutexLock mutex_;                // 互斥锁,受保护的成员变量Condition condition_ GUARDED_BY(mutex_); // 条件变量,受mutex_保护int count_ GUARDED_BY(mutex_);           // 倒计时计数,受mutex_保护
};

muduo::net::Buffer 类基础介绍

class Buffer : public muduo::copyable
{
public:/// 常量:预留的前置空间大小static const size_t kCheapPrepend = 8;/// 常量:初始缓冲区大小static const size_t kInitialSize = 1024;/// 显式构造函数,初始化缓冲区大小explicit Buffer(size_t initialSize = kInitialSize): buffer_(kCheapPrepend + initialSize),readerIndex_(kCheapPrepend),writerIndex_(kCheapPrepend) {}/// 交换缓冲区内容void swap(Buffer &rhs);/// 可读字节数size_t readableBytes() const;/// 可写字节数size_t writableBytes() const;/// 返回指向可读数据的指针const char *peek() const;/// 查找换行符(CRLF)的指针const char *findEOL() const;/// 从指定位置开始查找换行符(CRLF)的指针const char *findEOL(const char *start) const;/// 从缓冲区中取出指定长度的数据void retrieve(size_t len);/// 从缓冲区中取出一个64位整数void retrieveInt64();/// 从缓冲区中取出一个32位整数void retrieveInt32();/// 从缓冲区中取出一个16位整数void retrieveInt16();/// 从缓冲区中取出一个8位整数void retrieveInt8();/// 将缓冲区中的所有数据取出并转换为字符串string retrieveAllAsString();/// 将缓冲区中指定长度的数据取出并转换为字符串string retrieveAsString(size_t len);/// 向缓冲区追加字符串void append(const StringPiece &str);/// 向缓冲区追加指定数据和长度void append(const char * /*restrict*/ data, size_t len);/// 向缓冲区追加指定数据和长度void append(const void * /*restrict*/ data, size_t len);/// 返回指向可写数据的指针char *beginWrite();/// 返回指向可写数据的常量指针const char *beginWrite() const;/// 更新已写入的长度void hasWritten(size_t len);/// 向缓冲区追加一个64位整数void appendInt64(int64_t x);/// 向缓冲区追加一个32位整数void appendInt32(int32_t x);/// 向缓冲区追加一个16位整数void appendInt16(int16_t x);/// 向缓冲区追加一个8位整数void appendInt8(int8_t x);/// 从缓冲区中读取一个64位整数int64_t readInt64();/// 从缓冲区中读取一个32位整数int32_t readInt32();/// 从缓冲区中读取一个16位整数int16_t readInt16();/// 从缓冲区中读取一个8位整数int8_t readInt8();/// 查看缓冲区中的一个64位整数int64_t peekInt64() const;/// 查看缓冲区中的一个32位整数int32_t peekInt32() const;/// 查看缓冲区中的一个16位整数int16_t peekInt16() const;/// 查看缓冲区中的一个8位整数int8_t peekInt8() const;/// 在缓冲区前面添加一个64位整数void prependInt64(int64_t x);/// 在缓冲区前面添加一个32位整数void prependInt32(int32_t x);/// 在缓冲区前面添加一个16位整数void prependInt16(int16_t x);/// 在缓冲区前面添加一个8位整数void prependInt8(int8_t x);/// 在缓冲区前面添加指定数据和长度void prepend(const void * /*restrict*/ data, size_t len);private:std::vector<char> buffer_; // 缓冲区size_t readerIndex_;       // 读索引size_t writerIndex_;       // 写索引static const char kCRLF[]; // 换行符常量
};

接下来就是通过上面的接口来使用muduo库通过protobuf实现网络通信,这里我们简单写一个计算器

客户端

#include "muduo/proto/dispatcher.h"
#include "muduo/proto/codec.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpClient.h"
#include "muduo/net/EventLoopThread.h"
#include "muduo/base/CountDownLatch.h"#include "request.pb.h"
#include <memory>
#include <iostream>class Client
{
public:typedef std::shared_ptr<sslx::AddResponse> AddResponsePtr;typedef std::shared_ptr<sslx::SubResponse> SubResponsePtr;typedef std::shared_ptr<sslx::MulResponse> MulResponsePtr;typedef std::shared_ptr<sslx::DivResponse> DivResponsePtr;typedef std::shared_ptr<sslx::ErrorResponse> ErrorResponsePtr;typedef std::shared_ptr<google::protobuf::Message> MessagePtr;Client(const std::string& sip, int port):_latch(1),_client(_loopthread.startLoop(), muduo::net::InetAddress(sip, port), "Client"),_dispatcher(std::bind(&Client::onUnknownMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)){// 注册业务回复处理函数_dispatcher.registerMessageCallback<sslx::AddResponse>(std::bind(&Client::onAdd, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::SubResponse>(std::bind(&Client::onSub, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::MulResponse>(std::bind(&Client::onMul, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::DivResponse>(std::bind(&Client::onDiv, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::ErrorResponse>(std::bind(&Client::onError, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_client.setConnectionCallback(std::bind(&Client::onConnection, this, std::placeholders::_1));Connect();}void Connect(){_client.connect();_latch.wait();}void Add(int num1 , int num2){sslx::AddRequest req;req.set_num1(num1);req.set_num2(num2);send(req);}void Sub(int num1 , int num2){sslx::SubRequest req;req.set_num1(num1);req.set_num2(num2);send(req);}void Mul(int num1 , int num2){sslx::MulRequest req;req.set_num1(num1);req.set_num2(num2);send(req);}void Div(int num1 , int num2){sslx::DivRequest req;req.set_num1(num1);req.set_num2(num2);send(req);}
private:bool send(const google::protobuf::Message& msg){//连接状态正常,再发送,否则就返回falseif(_conn->connected()){_codec.send(_conn, msg);return true;}return false;}void onConnection(const muduo::net::TcpConnectionPtr& conn){if(conn->connected()){_conn = conn;_latch.countDown();}else{_conn.reset();  // 连接关闭时的操作}}void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& msg, muduo::Timestamp){LOG_INFO << "onUnknowMessage" << msg->GetTypeName();conn->shutdown();}void onAdd(const muduo::net::TcpConnectionPtr& conn, const AddResponsePtr& msg, muduo::Timestamp){std::cout << "加法结果 : " << msg->result() << std::endl; }void onSub(const muduo::net::TcpConnectionPtr& conn, const SubResponsePtr& msg, muduo::Timestamp){std::cout << "减法结果 : " << msg->result() << std::endl;}void onMul(const muduo::net::TcpConnectionPtr& conn, const MulResponsePtr& msg, muduo::Timestamp){std::cout << "乘法结果 : " << msg->result() << std::endl;}void onDiv(const muduo::net::TcpConnectionPtr& conn, const DivResponsePtr& msg, muduo::Timestamp){std::cout << "除法结果 : " << msg->result() << std::endl;}void onError(const muduo::net::TcpConnectionPtr& conn, const ErrorResponsePtr& msg, muduo::Timestamp){std::cout << "出现除零错误" << std::endl;}private:muduo::CountDownLatch _latch;  //主要同步主线程和网络线程,确保在连接建立之前不会发送请求muduo::net::EventLoopThread _loopthread; // 提供一个独立的事件循环,用于处理网络事件muduo::net::TcpConnectionPtr _conn;  // 保存当前的TcpConnectionPtr, 用于在连接建立之后发送消息muduo::net::TcpClient _client; // 管理客户端的连接和重连ProtobufDispatcher _dispatcher; // 用于根据消息的类型调用相应的处理函数ProtobufCodec _codec; // 负责Protobuf消息的编解码};int main()
{Client client("127.0.0.1", 8085);client.Add(11, 11);client.Sub(11, 11);sleep(1);return 0; 
}

服务端

#include <muduo/proto/codec.h>
#include "muduo/proto/dispatcher.h"
#include "muduo/base/Logging.h"
#include "muduo/base/Mutex.h"
#include "muduo/net/EventLoop.h"
#include "muduo/net/TcpServer.h"#include "request.pb.h"#include <memory>
#include <string>
#include <iostream>
#include <unordered_map>class Server
{
public:typedef std::shared_ptr<google::protobuf::Message> MessagePtr;typedef std::shared_ptr<sslx::AddRequest> AddRequestPtr;typedef std::shared_ptr<sslx::SubRequest> SubRequestPtr;typedef std::shared_ptr<sslx::MulRequest> MulRequestPtr;typedef std::shared_ptr<sslx::DivRequest> DivRequestPtr;Server(int port):_server(&_baseLoop, muduo::net::InetAddress("0.0.0.0", port), "Server", muduo::net::TcpServer::kReusePort),_dispatcher(std::bind(&Server::onUnknownMessage, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)),_codec(std::bind(&ProtobufDispatcher::onProtobufMessage, &_dispatcher, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3)){_dispatcher.registerMessageCallback<sslx::AddRequest>(std::bind(&Server::onAdd, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::SubRequest>(std::bind(&Server::onSub, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::MulRequest>(std::bind(&Server::onMul, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_dispatcher.registerMessageCallback<sslx::DivRequest>(std::bind(&Server::onDiv, this, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_server.setMessageCallback(std::bind(&ProtobufCodec::onMessage, &_codec, std::placeholders::_1, std::placeholders::_2, std::placeholders::_3));_server.setConnectionCallback(std::bind(&Server::onConnection, this, std::placeholders::_1));}void start(){_server.start();_baseLoop.loop();}
private:int Add(int num1, int num2){return num1 + num2;}int Sub(int num1, int num2){return num1 - num2;}int Mul(int num1, int num2){return num1 * num2;}int Div(int num1, int num2){if(num2 == 0){return INT_MIN;}return num1 / num2;}void onAdd(const muduo::net::TcpConnectionPtr& conn, const AddRequestPtr& msg, muduo::Timestamp){sslx::AddResponse resp;int ret = Add(msg->num1(), msg->num2());resp.set_result(ret);_codec.send(conn, resp);}void onSub(const muduo::net::TcpConnectionPtr& conn, const SubRequestPtr& msg, muduo::Timestamp){sslx::SubResponse resp;int ret = Sub(msg->num1(), msg->num2());resp.set_result(ret);_codec.send(conn, resp);}void onMul(const muduo::net::TcpConnectionPtr& conn, const MulRequestPtr& msg, muduo::Timestamp){sslx::MulResponse resp;int ret = Mul(msg->num1(), msg->num2());resp.set_result(ret);_codec.send(conn, resp);}void onDiv(const muduo::net::TcpConnectionPtr& conn, const DivRequestPtr& msg, muduo::Timestamp){sslx::DivResponse resp1;sslx::ErrorResponse resp2;int ret = Div(msg->num1(), msg->num2());if(ret ==INT_MIN){resp2.set_msg(std::to_string(ret));_codec.send(conn, resp2);}else{resp1.set_result(ret);_codec.send(conn, resp1);}}void onUnknownMessage(const muduo::net::TcpConnectionPtr& conn, const MessagePtr& message, muduo::Timestamp){LOG_INFO << "onUNknownMessage : " << message->GetTypeName();conn->shutdown();}void onConnection(const muduo::net::TcpConnectionPtr& conn){if(conn->connected()){LOG_INFO << "新连接建立成功";}else{LOG_INFO << "连接已关闭";}}private:muduo::net::EventLoop _baseLoop;muduo::net::TcpServer _server; //服务器对象ProtobufDispatcher _dispatcher; // 请求分发对象 --要向其中注册请求处理函数ProtobufCodec _codec;   // protobuf协议处理器--针对收到的请求数据进行protobuf协议处理};int main()
{Server server(8085);server.start();return 0;
}

        这里简单梳理一下网路的流程:在进行初始化的时候,客户端和服务端之间会对_codec和_dispatcher还有事件监控循环线程,在建立连接的时候,异步线程会通过setConnectionCallback的回调函数,onConnection函数中的countDown() 函数唤醒主线程开始进行业务处理,在进行业务处理的时候,会将请求通过send发到缓冲区中,响应报文回来之后会给setMessageCallback函数传递信号,然后由loop线程来接收到响应,调用相关的函数来对结果进行处理。

protobuf

syntax = "proto3";package sslx;message AddRequest
{int32 num1 = 1;int32 num2 = 2;
}message AddResponse
{int32 result = 1;
}message SubRequest
{int32 num1 = 1;int32 num2 = 2;
}message SubResponse
{int32 result = 1;
}message MulRequest
{int32 num1 = 1;int32 num2 = 2;
}message MulResponse
{int32 result = 1;
}message DivRequest
{int32 num1 = 1;int32 num2 = 2;
}message DivResponse
{int32 result = 1;
}message ErrorResponse
{string msg = 1;
}

makefile

all: client server
client: client.cc request.pb.cc mqthird/include/muduo/proto/codec.ccg++ -g -o $@ $^ -std=c++11 -I./mqthird/include -L./mqthird/lib -lmuduo_net -lmuduo_base -lpthread -lprotobuf -lz
server: server.cc request.pb.cc mqthird/include/muduo/proto/codec.ccg++ -g -o $@ $^ -std=c++11 -I./mqthird/include -L./mqthird/lib -lmuduo_net -lmuduo_base -lpthread -lprotobuf -lz
.PHONY:clean
clean:rm -rf server client

gitee

https://gitee.com/pu-mingbo/master.git

相关文章:

仿RabbitMq简易消息队列基础篇(Muduo库的使用)

TOC Muduo库简介 Muduo由陈硕⼤佬开发&#xff0c;是⼀个基于⾮阻塞IO和事件驱动的C⾼并发TCP⽹络编程库。他是一款基于主从Reactor模型的网络库&#xff0c;其使用的线程模型是one loop per thread, 所谓 one loop per thread 指的是&#xff1a; 一个线程只能有一个事件循…...

.net SqlSugarHelper

NuGet安装&#xff1a; SqlSugarCore using SqlSugar; using System; using System.Collections.Generic; using System.Linq; using System.Text; using System.Threading.Tasks;namespace Namespace {public class SqlSugarHelper{public string _connectionString Custom…...

“AI能不能代替某某职业”,到底谁在破防?

前几天&#xff0c;公司在午间分享时谈到一个有趣的辩题&#xff1a;“AI能不能代替产品经理”&#xff0c;不仅双方辩手打了个你来我往&#xff0c;就连下面的吃瓜群众也进入红温状态。 “AI能不能代替xx”已经成为一个普遍的话题&#xff0c;在某乎上随手一刷就是不同的职业…...

智慧图书馆:构建高效视频智能管理方案,提升图书馆个性化服务

一、背景分析 随着信息技术的飞速发展&#xff0c;智慧图书馆作为现代公共文化服务的重要载体&#xff0c;正逐步从传统的纸质阅读空间向数字化、智能化方向转型。其中&#xff0c;视频智能管理方案作为智慧图书馆安全管理体系的重要组成部分&#xff0c;不仅能够有效提升图书…...

React快速开发框架

本框架主要用于快速搭建项目 使用的基本库&#xff1a;webpackreactreact-routertypescript ps&#xff1a;有不足之处请多多包涵&#xff0c;提出意见或者建议 目的&#xff1a; 前端开发大多数时间是基于市面上比较流行的成品框架开始进行开发&#xff0c;途中遇到的问题大…...

【前端】记录各种控制台警告/bug

一、Element Plus 1、控制台警告&#xff1a;“Runtime directive used on component with non-element root node. The directives will not function as intended.” 错误原因&#xff1a;在 Vue 组件上使用了运行时指令&#xff08;指那些在运行时动态绑定到 DOM 元素上的指…...

猫咪掉毛严重怎么办?铲屎官家庭必备清理工具——宠物空气净化器

“毛&#xff0c;毛&#xff0c;毛&#xff0c;还是毛&#xff01;”铲屎官们每天都离不开和猫毛斗智斗勇&#xff0c;家里的每个角落都成了“战场”&#xff0c;掉毛的严重程度超乎想象。有时也在后悔当初怎么不养只无毛猫&#xff0c;而是把毛孩子接了回来&#xff0c;世上没…...

顺序表的实现——数据结构

线性表 文章目录 线性表线性表的定义和基本操作线性表的定义线性表的基本操作 线性表的顺序表示顺序表的定义顺序表的实现——静态分配顺序表的实现——动态分配顺序表的特点 线性表的定义和基本操作 线性表的定义 线性表&#xff08;Linear List&#xff09;的定义 ​ 线性…...

【模块化】CommonJS,AMD规范,CMD规范,ES6模块化

1. CommonJS Node.js基于CommonJS规范应运而生 1.1 commonjs规范语法导出模块 module.exports { a, b }1.2 commonjs规范语法引入模块 const mod require(./导出模块name)2. AMD 规范 RequireJS 是AMD规范的实现。是js文件和模块的加载器。 在没有单页应用&#xff08;angu…...

3.js - 顶点着色器、片元着色器的联系

1、定义与功能 顶点着色器 顶点着色器&#xff0c;是图形渲染管线中的第一个可编程阶段&#xff0c;它的主要任务是&#xff0c;处理从CPU发送到GPU的顶点数据&#xff0c;包括&#xff1a;1、顶点位置的变换&#xff08;如&#xff1a;模型空间 -> 世界空间 -> 视图控件…...

kotlin简介

Kotlin 是一种在 Java 虚拟机上运行的静态类型编程语言&#xff0c;被称之为 Android 世界的Swift&#xff0c;由 JetBrains 设计开发并开源。 Kotlin 可以编译成Java字节码&#xff0c;也可以编译成 JavaScript&#xff0c;方便在没有 JVM 的设备上运行。 在Google I/O 2017…...

Mintegral出海系列:解锁全球应用商店新增长路径

在全球化竞争的浪潮中&#xff0c;面对打法各异的应用和游戏品类&#xff0c;以及全球数百个环境不同的国家和地区&#xff0c;开发者们正面临着前所未有的挑战。Mintegral「出海ing」系列专题内容&#xff0c;助力出海开发者选准赛道探索新的增长路径。 据近期数据显示&#x…...

Qt 哈希加密之 QCryptographicHash

【写在前面】 QCryptographicHash 是 Qt 框架中提供的一个类&#xff0c;它用于实现加密散列函数&#xff0c;也就是我们常说的哈希函数。哈希函数能够将任意长度的数据转换为固定长度的哈希值&#xff0c;这个哈希值通常用于数据的完整性校验、密码存储等场景。 什么是哈希函数…...

渗透第二次作业

目录 简述rce漏洞 可能产生rce漏洞的函数 RCE代码执行漏洞示例 贷齐乐系统多处SQL注入漏洞 ​编辑 爆出库名 爆出表名 爆出表下的列名 查flag数据 简述rce漏洞 ​ rce漏洞&#xff0c;即远程代码执行和远程命令执行漏洞。这种漏洞允许攻击者在后台服务器上远程注入操作…...

42.【C语言】冒泡排序

目录&#xff1a; 冒泡排序 *核心思想 *分析 *代码 *优化 15.冒泡排序(bubble sort) *核心思想&#xff1a;两两相邻的元素进行比较&#xff0c;满足条件则两者交换 *分析 现要求升序排序 输入: 9 8 7 6 5 4 3 2 1 0 输出&#xff1a;0 1 2 3 4 5 6 7 8 9 下面展示一趟冒泡排…...

Linux安全与高级应用(七)深入Linux Shell脚本编程:循环与分支结构的高级应用

文章目录 深入Linux Shell脚本编程&#xff1a;循环与分支结构的高级应用一、循环结构详解1. for循环1.1 应用示例&#xff1a;检查主机状态 2. while循环2.1 应用示例&#xff1a;猜价格游戏 二、分支结构详解1. if语句1.1 单分支结构1.2 双分支结构1.3 多分支结构 2. case语句…...

python爬虫滑块验证及各种加密函数(基于ddddocr进行的一层封装)

git链接: https://github.com/JOUUUSKA/spider_toolsbox 这里写目录标题 一.识别验证码1、识别英文&#xff0b;数字验证码2、识别滑块验证码3、识别点选验证码 一.识别验证码 git链接: https://github.com/JOUUUSKA/spider_toolsbox 创作不易记得stars 1、识别英文&#xf…...

pytorch学习一(扩展篇):miniconda下载、安装、配置环境变量。miniconda创建多版本python环境。整理常用命令(亲测ok)

文章目录 前言一、miniconda和anaconda的关系1、Anaconda2、Miniconda3、总结 二、下载miniconda&#xff08;清华镜像链接&#xff09;三、安装miniconda1、安装2、或许要手动加载 ~/.bashrc 四、配置 命令1、查看anaconda安装博文2、取消默认进入conda&#xff08;base&#…...

说一下Android中的IdleHandler

IdleHandler 是 Android 中的一个接口&#xff0c;常用于在主线程空闲时执行一些低优先级的任务。 作用&#xff1a; 它提供了一种在主线程空闲时执行额外操作的机制&#xff0c;能够优化应用的性能和资源利用。 工作原理&#xff1a; 当主线程没有其他任务需要处理&#xff…...

Flake8 和 Autopep8 使用指南

Flake8 和 Autopep8 集成到 CI/CD 流程中&#xff0c;确保在代码提交和合并时自动进行检查和格式化&#xff0c;如果Autopep8格式化检查无法通过Flake8校验&#xff0c;说明pycodestyle版本依赖不兼容&#xff0c;参考文章&#xff1a;Flake8 与 Autopep8 兼容性指南 Flake8 使…...

OpenHarmony(数据)通信协议、数据存储—protobuf

介绍 ProtoBuf(protocol buffers) 是一种语言无关、平台无关、可扩展的序列化结构数据的方法&#xff0c;它可用于&#xff08;数据&#xff09;通信协议、数据存储等。,是一种灵活&#xff0c;高效&#xff0c;自动化机制的结构数据序列化方法比XML更小,更快,更为简单。 本项…...

vue3 依赖注入 vueRouter vuex

目录 01 依赖注入 02 组合式API里面的vueRouter 03 组合式API中的vuex的使用 01 依赖注入 使用场景&#xff1a; 有一个父组件&#xff0c;里头有子组件&#xff0c;有孙组件&#xff0c;有很多后代组件&#xff0c;共享父组件数据。 1.组先组件给后代组件传参 组先组件: 从…...

在Windows上用Visual Studio编译OpenCV

在Windows上编译开源项目&#xff0c;有时候让人痛不欲生&#xff0c;有时候却出奇地顺利。OpenCV属于后者。本文记录这次愉快的过程。 注&#xff1a;OpenCV&#xff08;Open Source Computer Vision Library&#xff09;是一个开源的计算机视觉和机器学习软件库。它提供了大…...

详解2024年最值得推荐的5款CRM软件:如何选择适合企业需求的CRM系统?

在文章开始之前&#xff0c;我们前来了解下&#xff1a;什么是CRM系统&#xff1f; CRM系统&#xff0c;即客户关系管理系统&#xff0c;顾名思义&#xff0c;它是企业用来管理和维护与客户之间关系的重要工具。通过CRM系统&#xff0c;企业能够全面了解客户需求&#xff0c;优…...

2024靠谱的网站建设公司推荐

在现在的互联网社会&#xff0c;一个企业的网站往往是潜在客户对该品牌的第一印象来源。也正因如此&#xff0c;选择一个靠谱的网站建设公司对于确保企业在线形象和功能性至关重要&#xff0c;作为建站行业从业人员&#xff0c;我分享几个选择网站建设公司时应考虑的几个关键因…...

第一天:Java基础与环境搭建

第一天&#xff1a;Java基础与环境搭建 1. 理解Java基本概念 了解Java语言的历史&#xff1a;Java是一种广泛使用的编程语言&#xff0c;由Sun Microsystems&#xff08;现被Oracle收购&#xff09;于1995年首次发布。认识Java的特性&#xff1a;包括面向对象、平台无关性&am…...

动画魔法秀:JavaScript前端动画实战指南

标题&#xff1a;动画魔法秀&#xff1a;JavaScript前端动画实战指南 在现代Web开发中&#xff0c;动画不仅能够提升用户体验&#xff0c;还能使网页更加生动有趣。JavaScript作为实现前端动画的重要工具之一&#xff0c;提供了多种方式来创建平滑且吸引人的动画效果。本文将详…...

实训日记day26

NAT服务配置 1.关闭防火墙和selinux [root2 ~]# setenforce 0 [root2 ~]# vim /etc/selinux/config [root2 ~]# systemctl stop firewalld [root2 ~]# systemctl disable firewalld 2.安装nginx &#xff08;web1和web2&#xff09; [root2 ~]# yum install -y gcc-c pcre pcr…...

自定义实现一个 Redis 客户端

要自定义实现一个 Redis 客户端并支持密码认证&#xff0c;你可以使用 TCP socket 直接与 Redis 服务器进行通信。下面是如何通过 Java 自定义实现一个简单的 Redis 客户端的详细示例&#xff0c;包括如何发送密码进行认证。 Redis 协议概述 Redis 使用一种称为 RESP&#xf…...

sql注入——sqlilabs16-26

文章目录 less-163.注入 less-172.数据库名2.1 floor报错注入数据库名 3.查到数据表3.1floor 报错注入数据表 4.查取列名4.1 floor报错注入 列名 5.查取内容 less-181.添加X-Forwarded-For测试2修改User-Agent测试3.查数据表名4.查数据列5.查取数据 less-192.查数据库3.查数据表…...