【C++】從零實現Json-Rpc框架(2)

目錄

JsonCpp庫

1.1- Json數據格式

1.2 - JsonCpp介紹

? 序列化接口?

? 反序列化接口

1.3 - Json序列化實踐

JsonCpp使用

Muduo庫

2.1 - Muduo庫是什么

2.2 - Muduo庫常見接口介紹

TcpServer類基礎介紹

EventLoop類基礎介紹

TcpConnection類基礎介紹

TcpClient類基礎介紹

Buffer類基礎介紹

2.3 - Muduo庫快速上手

英譯漢TCP服務器

?編輯

英譯漢客戶端

Makefile


項目匯總:uyeonashi的博客-CSDN博客

項目源碼:https://gitee.com/uyeonashi/project

本篇文章是對第三方庫的介紹和使用(JsonCpp庫,Muduo庫)!

JsonCpp庫

1.1- Json數據格式

Json 是一種數據交換格式,它采用完全獨立于編程語言的文本格式來存儲和表示數據。

例如: 我們想表示一個同學的學生信息

C 代碼表示:

char *name = "xx";
int age = 18;
float score[3] = {88.5, 99, 58};

Json 表示:

{"姓名" : "xx","年齡" : 18,"成績" : [88.5, 99, 58],"愛好" :{"書籍" : "西游記","運動" : "打籃球"}
}

Json 的數據類型包括對象,數組,字符串,數字等。

  • 對象:使用花括號{ } 括起來的表示一個對象
  • 數組:使用中括號[ ] 括起來的表示一個數組
  • 字符串:使用常規雙引號" " 括起來的表示一個字符串
  • 數字:包括整形和浮點型,直接使用

1.2 - JsonCpp介紹

Jsoncpp 庫主要是用于實現Json 格式數據的序列化和反序列化,它實現了將多個數據對象組織成為json 格式字符串,以及將Json 格式字符串解析得到多個數據對象的功能。

Jsoncpp的介紹:3個類

? ? ? ? Json::Value類:中間數據存儲類

? ? ? ? 如果要將數據進行序列化,就需要先存儲到Json::Value對象當中

? ? ? ? 如果要將數據進行反序列化,就是解析后,將數據對象放入到Json::Value對象當中

先看一下Json 數據對象類的表示

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();
};

Jsoncpp 庫主要借助三個類以及其對應的少量成員函數完成序列化及反序列化

? 序列化接口?

Json::StreamWriter類:用于進行數控序列化

? ? ? ? Json::StreamWriter::write() 序列化函數

Json::StreamWriterBuilder類:Json::StreamWriter工廠類 —?用于生成Json::StreamWriter對象

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;
}

? 反序列化接口

Json::CharReader類:反序列化類

? ? ? ? Json::CharReader::parse() 反序列化函數

Json::CharReaderBuilder類:Json::CharReader工廠類 — 用于生產Json::CharReader對象

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;
}

1.3 - Json序列化實踐

JsonCpp使用

#include<iostream>
#include<memory>
#include<string>
#include<sstream>
#include<jsoncpp/json/json.h>//實現數據的序列化
bool serialize(const Json::Value &val,std::string &body)
{std::stringstream ss;//先實例化一個工廠類對象Json::StreamWriterBuilder swb;//通過工廠生產派生類對象std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());int ret = sw->write(val,&ss);if(ret != 0){std::cout << "json serialize failed\n";return false;}body = ss.str();return true;
}//實現json的反序列化
bool unserialize(const std::string &body,Json::Value &val)
{//實例化工廠對象Json::CharReaderBuilder crb;//生產Charreader對象std::string errs;std::unique_ptr<Json::CharReader> cr(crb.newCharReader());bool ret = cr->parse(body.c_str(),body.c_str()+body.size(),&val,&errs);if(ret == false){std::cout << "Json unserialize failed: " << errs << std::endl;return false;}return true;
}int main()
{const char *name = "小明";int age = 18;const char *sex = "男";float score[3] = {88,77.5,66};Json::Value student;student["姓名"] = name;student["年齡"] = age;student["性別"] = sex;student["成績"].append(score[0]);student["成績"].append(score[1]);student["成績"].append(score[2]);Json::Value fav;fav["書籍"] = "西游記";fav["運動"] = "打籃球";student["愛好"] = fav;std::string body;serialize(student,body);std::cout << body << std::endl;std::string str = R"({"姓名":"小黑","年齡":19,"成績":[32,45.5,56]})";Json::Value stu;bool ret = unserialize(str,stu);if(ret == false)return -1;std::cout << "姓名: " << stu["姓名"].asString() << std::endl;std::cout << "年齡:"  << stu["年齡"].asInt() << std::endl;int sz = stu["成績"].size();for(int i = 0; i < sz; i++){std::cout << "成績:" << stu["成績"][i].asFloat() << std::endl;} return 0;
}

編譯運行程序查看序列化和反序列化結果?

并在在這將其封裝了一下,方便我們后邊的使用


Muduo庫

2.1 - Muduo庫是什么

Muduo由陳碩大佬開發,是一個基于非阻塞IO和事件驅動的C++高并發TCP網絡編程庫。 它是一款基于主從Reactor模型的網絡庫,其使用的線程模型是one loop per thread, 所謂one loop per thread指的是:

  • 一個線程只能有一個事件循環(EventLoop), 用于響應計時器和IO事件
  • 一個文件描述符只能由一個線程進行讀寫,換句話說就是一個TCP連接必須歸屬于某EventLoop管理

2.2 - Muduo庫常見接口介紹

TcpServer類基礎介紹

TcpServer{

? ? ? ? void start();? //啟動服務

? ? ? ? setConnectionCallback(); //設置連接建立 / 關閉時的回調函數

? ? ? ? setMessageCallback();//設置消息處理回調函數

};

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; }
};

EventLoop類基礎介紹

EvenLoop{

? ? ? ? void loop(); //開始事件監控事件循環

? ? ? ? void? quit(); //停止循環

? ? ? ? Timerld runAfter(delay,cb); //定時任務

};

class EventLoop : noncopyable
{
public:// Loops forever.// Must be called in the same thread as creation of the object.void loop();// Quits loop.// This is not 100% thread safe, if you call through a raw pointer,///better to call through shared_ptr<EventLoop> for 100% safety.void quit();TimerId runAt(Timestamp time, TimerCallback cb);// Runs callback after @c delay seconds.// Safe to call from other threads.TimerId runAfter(double delay, TimerCallback cb);// Runs callback every @c interval seconds.// Safe to call from other threads.TimerId runEvery(double interval, TimerCallback cb);// Cancels the timer.// Safe to call from other threads.void cancel(TimerId timerId);
private:std::atomic<bool> quit_;std::unique_ptr<Poller> poller_;mutable MutexLock mutex_;std::vector<Functor> pendingFunctors_ GUARDED_BY(mutex_);
};

TcpConnection類基礎介紹

TcpConnection{

? ? ? ? void send(std::string &msg); //發送數據

? ? ? ? bool connected(); //判斷當前連接是否連接正常

? ? ? ? shutdown down(); //關閉連接

}

class TcpConnection : noncopyable,public std::enable_shared_from_this<TcpConnection>
{
public:
// Constructs a TcpConnection with a connected sockfd
//
// User should not create this object.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; }void send(string&& message); // C++11void send(const void* message, int len);void send(const StringPiece& message);// void send(Buffer&& message); // C++11void send(Buffer* message); // this one will swap datavoid shutdown(); // NOT thread safe, no simultaneous callingvoid 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_;
};

TcpClient類基礎介紹

TcpClient{

? ? ? ? void connect(); //連接服務器

????????void disconnect(); //關閉連接

? ? ? ? TcpConnectionPtr connection() const; // 獲取客戶端對應的TcpConnection連接

? ? ? ? Muduo庫的客戶端也是通過Eventloop進行IO事件監控IO處理的

? ? ? ? void setConnectionCallback(ConnectionCallback cb); //連接建立成功 / 關閉的回調處理

? ? ? ? void setMessageCallback(MessageCallback cb); //收到消息的回調處理

}?

CountDownLatch{ //做計數同步操作的類

? ? ? ? void wait(); //計數大于0則阻塞

? ? ? ? countDown(); //計數--,為0時喚醒wait

}

因為Client的connect接口是一個非阻塞操作,所以可能出現移走以外情況:connect連接還沒有完全建立的情況下,調用connection接口獲取連接,send發送數據??

class TcpClient : noncopyable
{
public:// TcpClient(EventLoop* loop);// TcpClient(EventLoop* loop, const string& host, uint16_t port);TcpClient(EventLoop* loop,const InetAddress& serverAddr,const string& nameArg);~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_);
};/*需要注意的是,因為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_);int count_ GUARDED_BY(mutex_);};

Buffer類基礎介紹

Buffer {

? ? ? ? size_t readableBytes();? //獲取緩沖區可讀數據大小

? ? ? ? const char* peek();? ? // 獲取緩沖區的起始地址

? ? ? ? int32_t peeklnt32() const;? //嘗試從緩沖區獲取4字節數據,進行網絡字節序轉換為整形,但數據并不從緩沖區刪除

? ? ? ? void retrieventlnt32();? //數據讀取位置向后偏移4字節,本質上就是刪除起始位置的4字節數據

? ? ? ? int32_t readlnt32();?

? ? ? ? string retrieveAllAsString(); //從緩沖區取出所有數據,當做string返回,并刪除緩沖區中的數據

? ? ? ? string retrieveAsString(size_t len),從緩沖區中取出len長度的數據,當做string返回,并刪除緩沖區中的數據

}

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() constsize_t writableBytes() constconst char* peek() constconst char* findEOL() constconst char* findEOL(const char* start) constvoid retrieve(size_t len)void retrieveInt64()void retrieveInt32()void retrieveInt16()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() constvoid hasWritten(size_t len)void appendInt64(int64_t x)void appendInt32(int32_t x)void appendInt16(int16_t x)void appendInt8(int8_t x)int64_t readInt64()int32_t readInt32()int16_t readInt16()int8_t readInt8()int64_t peekInt64() constint16_t peekInt16() constint8_t peekInt8() constvoid prependInt64(int64_t x)void prependInt32(int32_t x)void prependInt16(int16_t x)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[];
};

2.3 - Muduo庫快速上手

我們使用Muduo網絡庫來實現一個簡單英譯漢服務器和客戶端 快速上手Muduo庫

英譯漢TCP服務器

包含頭文件時指定路徑

// 實現一個翻譯服務器,客戶端發來一個英語單詞,返回一個漢語詞典#include <muduo/net/TcpServer.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/Buffer.h>
#include <iostream>
#include <string>
#include <unordered_map>class DictServer
{
public:DictServer(int port): _server(&_baseloop, muduo::net::InetAddress("0.0.0.0", port), "DictServer", muduo::net::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 muduo::net::TcpConnectionPtr &conn){if (conn->connected())std::cout << "連接建立!\n";elsestd::cout << "連接斷開!\n";}void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp){static std::unordered_map<std::string, std::string> dict_map = {{"hello", "你好"},{"world", "世界"},{"bite", "比特"}};std::string msg = buf->retrieveAllAsString();std::string res;auto it = dict_map.find(msg);if (it != dict_map.end()){res = it->second;}else{res = "未知單詞";}conn->send(res);}private:muduo::net::EventLoop _baseloop;muduo::net::TcpServer _server;
};int main()
{DictServer server(9090);server.start();return 0;
}

英譯漢客戶端

#include <muduo/net/TcpClient.h>
#include <muduo/net/EventLoop.h>
#include <muduo/net/TcpConnection.h>
#include <muduo/net/EventLoopThread.h>
#include <muduo/net/Buffer.h>
#include <muduo/base/CountDownLatch.h>
#include <memory>
#include <iostream>
#include <string>class DictClient
{
public:DictClient(const std::string &sip, int sport):_baseloop(_loopthread.startLoop()) ,_downlatch(1), _client(_baseloop, muduo::net::InetAddress(sip, sport), "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();_downlatch.wait();}bool send(const std::string &msg){if(_conn->connected() == false){std::cout << "鏈接已斷開,發送數據失敗!\n";return false;}_conn->send(msg);//_baseloop.loop();  //開始事件循環監控 -- 內部是個死循環,客戶端不能直接使用return true;}private:void onConnection(const muduo::net::TcpConnectionPtr &conn){if (conn->connected()){std::cout << "連接建立!\n";_downlatch.countDown(); // 計數--,為0時喚醒阻塞_conn = conn;}else{std::cout << "連接斷開!\n";_conn.reset();}}void onMessage(const muduo::net::TcpConnectionPtr &conn, muduo::net::Buffer *buf, muduo::Timestamp){std::string res = buf->retrieveAllAsString();std::cout << res << std::endl;}private:muduo::net::TcpConnectionPtr _conn;muduo::CountDownLatch _downlatch;muduo::net::EventLoopThread _loopthread;muduo::net::EventLoop *_baseloop;muduo::net::TcpClient _client;
};int main()
{DictClient client("127.0.0.1",9090);while(1){std::string msg;std::cin >> msg;client.send(msg);}return 0;
}

Makefile

CFLAG=-I ../../build/release-install-cpp11/include/
LFLAF=-L ../../build/release-install-cpp11/lib -lmuduo_net -lmuduo_base -pthread
all: server client
server:server.cppg++ $(CFLAG) -o $@ $^ $(LFLAF) -std=c++11
client:client.cppg++ $(CFLAG) -o $@ $^ $(LFLAF) -std=c++11


?C++11 異步操作?

3.1 - std::future

介紹

std::future是C++11標準庫中的一個模板類,它表示一個異步操作的結果。當我們在多線程編程中使用異步任務時,std::future可以幫助我們在需要的時候獲取任務的執行結果。std::future的一個重要特性是能夠阻塞當前線程,直到異步操作完成,從而確保我們在獲取結果時不會遇到未完成的操作。

應用場景

  • 異步任務: 當我們需要在后臺執行一些耗時操作時,如網絡請求或計算密集型任務等,std::future可以用來表示這些異步任務的結果。通過將任務與主線程分離,我們可以實現任務的并行處理,從而提高程序的執行效率
  • 并發控制: 在多線程編程中,我們可能需要等待某些任務完成后才能繼續執行其他操作。通過使用std::future,我們可以實現線程之間的同步,確保任務完成后再獲取結果并繼續執行后續操作
  • 結果獲取:std::future提供了一種安全的方式來獲取異步任務的結果。我們可以使用std::future::get()函數來獲取任務的結果,此函數會阻塞當前線程,直到異步操作完成。這樣,在調用get()函數時,我們可以確保已經獲取到了所需的結果

用法示例

使用 std::async關聯異步任務

std::async是一種將任務與std::future關聯的簡單方法。它創建并運行一個異步任務,并返回一個與該任務結果關聯的std::future對象。默認情況下,std::async是否啟動一個新線程,或者在等待future時,任務是否同步運行都取決于你給的 參數。這個參數為std::launch類型:

? ? ? ??std::launch::deferred 表明該函數會被延遲調用,直到在future上調用get()或者wait()才會開始執行任務
????????std::launch::async 表明函數會在自己創建的線程上運行
????????std::launch::deferred | std::launch::async 內部通過系統等條件自動選擇策略

#include <iostream>
#include <thread>
#include <future>int Add(int num1, int num2) 
{std::cout << "into add!\n";return num1 + num2;
}int main()
{//std::launch::async策略:內部創建一個線程執行函數,函數運行結果通過future獲取//std::launch::deferred策略:同步策略,獲取結果的時候再去執行任務//std::future<int> res = std::async(std::launch::deferred, Add, 11, 22);std::future<int> res = std::async(std::launch::async, Add, 11, 22);//進行了一個異步非阻塞調用std::this_thread::sleep_for(std::chrono::seconds(1));std::cout << "--------------------------\n";//std::future<int>::get()  用于獲取異步任務的結果,如果還沒有結果就會阻塞std::cout << res.get() << std::endl;return 0;
}

使用std::packaged_task和std::future配合

std::packaged_task就是將任務和 std::feature 綁定在一起的模板,是一種對任務的封裝。我們可以通過std::packaged_task對象獲取任務相關聯的std::feature對象,通過調用get_future()方法獲得。
std::packaged_task的模板參數是函數簽名
可以把std::future和std::async看成是分開的, 而 std::packaged_task則是一個整體。

#include<iostream>
#include<future>
#include<thread>
#include<memory>int Add(int num1, int num2)
{std::cout << "into add!\n";return num1 + num2;
}int main()
{//1. 封裝任務auto task = std::make_shared<std::packaged_task<int(int, int)>>(Add);//2. 獲取任務包關聯的future對象std::future<int> res = task->get_future();std::thread thr([&task](){(*task)(11,22);});//4. 獲取結果std::cout << res.get() << std::endl;thr.join();return 0;
}

使用std::promise和std::future配合

std::promise提供了一種設置值的方式,它可以在設置之后通過相關聯的std::future對象進行讀取。換種說法就是之前說過std::future可以讀取一個異步函數的返回值了, 但是要等待就緒, 而std::promise就提供一種 方式手動讓 std::future就緒

#include<iostream>
#include<future>
#include<thread>
#include<memory>int Add(int num1,int num2)
{std::cout << "into add!\n";return num1+num2;
}int main()
{//1.在使用的時候,先實例化一個指定結果的promise對象std::promise<int> pro;//2. 通過promise對象獲取關聯future對象std::future<int> res = pro.get_future();//3. 在任意位置給promise設置數據,就可以通過關聯的future獲取到這個設置的數據了std::thread thr([&pro](){int sum = Add(11,22);pro.set_value(sum);});std::cout << res.get() << std::endl;thr.join();return 0;
}

std::future本質上不是一個異步任務,而是一個輔助我們獲取異步任務結果的東西
std::future并不能單獨使用,需要搭配一些能夠執行異步任務的模版類或函數一起使用
異步任務搭配使用

  • ?std::async函數模版:異步執行一個函數,返回一個future對象用于獲取函數結果
  • ?std::packaged_task類模版:為一個函數生成一個異步任務結果(可調用對象),用于在其他線程中執行
  • ? std::promise類模版:實例化的對象可以返回一個future,在其他線程中相promise對象設置數據,其他線程的關聯future就可以獲取數據

std::async是一個模版函數,內部會創建線程執行異步操作
std::packaged_task是一個模版類,是一個任務包,是對一個函數進行二次封裝,封裝乘一個課調用對象作為任務放到其他線程執行的
任務包封裝好了以后,可以在任意位置進行調用,通過關聯的future來獲取執行結果


本篇完,下篇見!

本文來自互聯網用戶投稿,該文觀點僅代表作者本人,不代表本站立場。本站僅提供信息存儲空間服務,不擁有所有權,不承擔相關法律責任。
如若轉載,請注明出處:http://www.pswp.cn/news/900230.shtml
繁體地址,請注明出處:http://hk.pswp.cn/news/900230.shtml
英文地址,請注明出處:http://en.pswp.cn/news/900230.shtml

如若內容造成侵權/違法違規/事實不符,請聯系多彩編程網進行投訴反饋email:809451989@qq.com,一經查實,立即刪除!

相關文章

語文常識推翻百年“R完備、封閉”論

?語文常識推翻百年“R完備、封閉”論 黃小寧 李四光&#xff1a;迷信權威等于扼殺智慧。語文常識表明從西方傳進來的數學存在重大錯誤&#xff1a;將無窮多各異數軸誤為同一軸。 復平面z各點z的對應點zk的全體是zk平面。z面平移變換為zk&#xff08;k是非1正實常數&#xf…

【Vue】 核心特性實戰解析:computed、watch、條件渲染與列表渲染

目錄 一、計算屬性&#xff08;computed&#xff09; ? 示例&#xff1a; 計算屬性-methods實現&#xff1a;在插值模塊里&#xff0c;實現函數的調用功能 計算屬性-computed的實現&#xff1a; 計算屬性-簡寫&#xff1a; ? 特點&#xff1a; ?? 與 methods 的區別…

二叉樹 遞歸

本篇基于b站靈茶山艾府的課上例題與課后作業。 104. 二叉樹的最大深度 給定一個二叉樹 root &#xff0c;返回其最大深度。 二叉樹的 最大深度 是指從根節點到最遠葉子節點的最長路徑上的節點數。 示例 1&#xff1a; 輸入&#xff1a;root [3,9,20,null,null,15,7] 輸出&…

與 AI 共舞:解鎖自我提升的無限可能

與 AI 共舞&#xff1a;解鎖自我提升的無限可能 在數字化浪潮的洶涌沖擊下&#xff0c;人工智能&#xff08;AI&#xff09;正以前所未有的速度重塑著世界的每一個角落。從日常生活的點滴便利到復雜工作的高效推進&#xff0c;AI 的力量無處不在。然而&#xff0c;面對 AI 的強…

【網絡安全論文】筑牢局域網安全防線:策略、技術與實戰分析

【網絡安全論文】筑牢局域網安全防線:策略、技術與實戰分析 簡述一、引言1.1 研究背景1.2 研究目的與意義1.3 國內外研究現狀1.4 研究方法與創新點二、局域網網絡安全基礎理論2.1 局域網概述2.1.1 局域網的定義與特點2.1.2 局域網的常見拓撲結構2.2 網絡安全基本概念2.2.1 網絡…

MoE Align Sort在醫院AI醫療領域的前景分析(代碼版)

MoE Align & Sort技術通過優化混合專家模型(MoE)的路由與計算流程,在醫療數據處理、模型推理效率及多模態任務協同中展現出顯著優勢,其技術價值與應用意義從以下三方面展開分析: 一、方向分析 1、提升醫療數據處理效率 在醫療場景中,多模態數據(如醫學影像、文本…

[ctfshow web入門] web4

前置知識 robots.txt是機器人協議&#xff0c;在使用爬蟲爬取網站內容時應該遵循的協議。協議并不能阻止爬蟲爬取&#xff0c;更像是一種道德規范。 假設robots.txt中寫道 Disallow: /admind.php&#xff0c;那我就暴露了自己的后臺&#xff0c;這屬于信息泄漏&#xff0c;攻擊…

innodb如何實現mvcc的

InnoDB 實現 MVCC&#xff08;多版本并發控制&#xff09;的機制主要依賴于 Undo Log&#xff08;回滾日志&#xff09;、Read View&#xff08;讀視圖&#xff09; 和 隱藏的事務字段。以下是具體實現步驟和原理&#xff1a; 1. 核心數據結構 InnoDB 的每一行數據&#xff08…

coding ability 展開第九幕(位運算——進階篇)超詳細!!!!

文章目錄 前言丟失的數字兩整數之和只出現一次的數字II消失的兩個數字總結 前言 上一篇博客&#xff0c;我們已經把位運算的基礎知識&#xff0c;以及基本運算都掌握啦 上次的習題還是讓人意猶未盡&#xff0c;今天我們來嘗試一下難一點的題目 位運算熟練起來真的讓人覺得做題是…

【數據結構篇】算法征途:穿越時間復雜度與空間復雜度的迷霧森林

文章目錄 【數據結構篇】算法征途&#xff1a;穿越時間復雜度與空間復雜度的迷霧森林 一、 什么是算法1. 算法的定義1.1 算法的五個特征1.2 好算法的特質 2. 時間復雜度3. 空間復雜度 【數據結構篇】算法征途&#xff1a;穿越時間復雜度與空間復雜度的迷霧森林 &#x1f4ac;歡…

Logo語言的系統監控

Logo語言的系統監控 引言 在信息技術飛速發展的時代&#xff0c;系統監控成為了確保計算機系統和網絡平穩運行的重要手段。系統監控不僅可以實時跟蹤系統的性能、資源使用情況和安全風險等&#xff0c;還能夠在出現問題時及時發出警報&#xff0c;從而避免潛在的故障和損失。…

STP學習

{所有內容均來自于西安歐鵬的陳俊老師} STP生成樹 當二層交換機意外成環路的時候會發生&#xff1a; 1.廣播風暴&#xff1a;當廣播幀進入環路時&#xff0c;會被不斷復制并傳輸&#xff0c;導致網絡中的廣播流量急劇增加&#xff0c;消耗大量的網絡帶寬&#xff0c;降低網絡…

使用RKNN進行yolo11-cls部署

文章目錄 概要制作數據集模型訓練onnx導出rknn導出概要 YOLO(You Only Look Once)是一系列高效的目標檢測算法,其核心思想是將目標檢測任務轉化為一個回歸問題,通過單個神經網絡直接在圖像上預測邊界框和類別概率。當將其用于分類任務時,會去除目標檢測相關的邊界框預測部…

【MySQL】01.MySQL環境安裝

注意&#xff1a;在MYSQL的安裝與卸載中&#xff0c;需要使用root用戶進行。 一、卸載不必要的環境 ? 查看是否有運行的服務 [rootVM-24-10-centos etc]# ps axj |grep mysql1 22030 22029 22029 ? -1 Sl 27 0:00 /usr/sbin/mysqld --daemonize --pid-fi…

程序化廣告行業(59/89):廣告驗證與反作弊實戰技巧

程序化廣告行業&#xff08;59/89&#xff09;&#xff1a;廣告驗證與反作弊實戰技巧 大家好&#xff01;在程序化廣告領域&#xff0c;想要做好投放&#xff0c;除了了解基本的架構和原理&#xff0c;還得掌握一些關鍵的技能&#xff0c;比如廣告驗證和反作弊。今天就和大家一…

矢量瓦片切片工具

1.geoserver 可以生成geojson mvt(pbf) tojson 三種格式矢量瓦片 2.mapbox的tippecanoe 可以生成pbf矢量瓦片&#xff0c;文件夾形式和mbtiles兩種 3.TileStache python工具&#xff0c;可以生成geojson瓦片 4.PostGis mapbox插件可以生成pbf瓦片&#xff0c;據說是動態切片…

Windows 系統 Git 2.15.0 (64位) 下載與安裝教程

1. 下載 Git 2.15.0 (64位) 安裝包 下載地址&#xff1a;https://pan.quark.cn/s/f817ab9285dc 2. 運行安裝程序 雙擊下載的 Git-2.15.0-64-bit.exe。 如果系統提示安全警告&#xff0c;選擇 “運行”&#xff08;確認來源可信&#xff09;。 3. 安裝向導設置 按以下步驟配…

MCP服務器:AI與外部工具交互的橋梁——Python和代理AI工具集成指南

&#x1f9e0; 向所有學習者致敬&#xff01; “學習不是裝滿一桶水&#xff0c;而是點燃一把火。” —— 葉芝 我的博客主頁&#xff1a; https://lizheng.blog.csdn.net &#x1f310; 歡迎點擊加入AI人工智能社區&#xff01; &#x1f680; 讓我們一起努力&#xff0c;共創…

AIGC8——大模型生態與開源協作:技術競逐與普惠化浪潮

引言&#xff1a;大模型發展的分水嶺時刻 2024年成為AI大模型發展的關鍵轉折點&#xff1a;OpenAI的GPT-4o實現多模態實時交互&#xff0c;中國DeepSeek-MoE-16b模型以1/8成本達到同類90%性能&#xff0c;而開源社區如Mistral、LLama 3持續降低技術門檻。這場"閉源商業巨…

Muduo網絡庫實現 [十五] - HttpContext模塊

目錄 設計思路 類的設計 解碼過程 模塊的實現 私有接口 請求函數 解析函數 公有接口 疑惑點 設計思路 記錄每一次請求處理的進度&#xff0c;便于下一次處理。 上下文模塊是Http協議模塊中最重要的一個模塊&#xff0c;他需要記錄每一次請求處理的進度&#xff0c;需…