Qt 遠程過程調用(RPC)實現方案

在分布式系統開發中,遠程過程調用(RPC)是實現跨進程、跨機器通信的重要技術。Qt 作為一個強大的跨平臺框架,提供了多種 RPC 實現方案,能夠滿足不同場景下的通信需求。本文將深入探討 Qt 中 RPC 的各種實現方式,包括 Qt Remote Objects、自定義協議實現、第三方庫集成等,并分析各自的優缺點和適用場景。

一、Qt Remote Objects 框架

1. 基礎概念與架構

Qt Remote Objects 是 Qt 官方提供的 RPC 框架,基于信號槽機制實現跨進程、跨網絡的對象通信。它采用模型-代理架構:

  • 源模型(Source Model):提供實際功能的對象
  • 遠程代理(Remote Proxy):客戶端側的代理對象,鏡像源模型的接口
// 定義接口(.rep 文件)
class MyInterface {PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)SIGNAL(valueChanged(int))SLOT(increment())
};
2. 服務端實現
#include <QCoreApplication>
#include <QtRemoteObjects/QRemoteObjectHost>
#include "myinterface_replica.h"class MyInterfaceSource : public QObject {Q_OBJECTQ_PROPERTY(int value READ value WRITE setValue NOTIFY valueChanged)
public:explicit MyInterfaceSource(QObject *parent = nullptr) : QObject(parent), m_value(0) {}int value() const { return m_value; }void setValue(int value) {if (m_value != value) {m_value = value;emit valueChanged(m_value);}}public slots:void increment() {setValue(m_value + 1);}signals:void valueChanged(int value);private:int m_value;
};int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 創建主機QRemoteObjectHost host(QUrl("local:replica"));// 創建源對象MyInterfaceSource src;// 注冊源對象host.enableRemoting(&src, "MyInterface");return a.exec();
}
3. 客戶端實現
#include <QCoreApplication>
#include <QtRemoteObjects/QRemoteObjectNode>
#include "myinterface_replica.h"int main(int argc, char *argv[]) {QCoreApplication a(argc, argv);// 創建客戶端節點QRemoteObjectNode node;node.connectToNode(QUrl("local:replica"));// 獲取代理對象QScopedPointer<MyInterfaceReplica> replica(node.acquire<MyInterfaceReplica>());// 連接信號QObject::connect(replica.data(), &MyInterfaceReplica::valueChanged, [](int value) {qDebug() << "Value changed to:" << value;});// 調用遠程方法replica->increment();return a.exec();
}

二、基于自定義協議的 RPC 實現

1. 消息協議設計
// 消息頭結構
struct MessageHeader {quint32 magicNumber;    // 魔數,用于協議識別quint32 messageId;      // 消息 IDquint32 methodId;       // 方法 IDquint32 payloadSize;    // 負載大小
};// 消息處理器
class RpcMessageHandler : public QObject {Q_OBJECT
public:explicit RpcMessageHandler(QIODevice *device, QObject *parent = nullptr): QObject(parent), m_device(device) {connect(m_device, &QIODevice::readyRead, this, &RpcMessageHandler::onReadyRead);}signals:void methodCallReceived(quint32 methodId, const QByteArray &params);void responseReceived(quint32 messageId, const QByteArray &result);public slots:void sendMethodCall(quint32 methodId, const QByteArray &params) {static quint32 nextMessageId = 1;MessageHeader header;header.magicNumber = 0x12345678;header.messageId = nextMessageId++;header.methodId = methodId;header.payloadSize = params.size();QDataStream stream(m_device);stream.setVersion(QDataStream::Qt_5_15);stream << header.magicNumber;stream << header.messageId;stream << header.methodId;stream << header.payloadSize;stream.writeRawData(params.data(), params.size());}private slots:void onReadyRead() {// 解析消息頭if (m_device->bytesAvailable() < sizeof(MessageHeader))return;MessageHeader header;QDataStream stream(m_device);stream.setVersion(QDataStream::Qt_5_15);stream >> header.magicNumber;stream >> header.messageId;stream >> header.methodId;stream >> header.payloadSize;// 驗證魔數if (header.magicNumber != 0x12345678) {qDebug() << "Invalid magic number";return;}// 讀取消息體if (m_device->bytesAvailable() < header.payloadSize)return;QByteArray payload = m_device->read(header.payloadSize);// 分發消息if (header.methodId != 0) {emit methodCallReceived(header.methodId, payload);} else {emit responseReceived(header.messageId, payload);}}private:QIODevice *m_device;
};
2. 服務端實現
class RpcServer : public QObject {Q_OBJECT
public:explicit RpcServer(quint16 port, QObject *parent = nullptr): QObject(parent), m_server(new QTcpServer(this)) {connect(m_server, &QTcpServer::newConnection, this, &RpcServer::onNewConnection);if (!m_server->listen(QHostAddress::Any, port)) {qDebug() << "Server could not start!";} else {qDebug() << "Server started!";}}void registerMethod(quint32 methodId, std::function<QByteArray(const QByteArray&)> handler) {m_methodHandlers[methodId] = handler;}private slots:void onNewConnection() {QTcpSocket *socket = m_server->nextPendingConnection();RpcMessageHandler *handler = new RpcMessageHandler(socket, this);connect(handler, &RpcMessageHandler::methodCallReceived, this, [this, handler](quint32 methodId, const QByteArray &params) {if (m_methodHandlers.contains(methodId)) {QByteArray result = m_methodHandlers[methodId](params);handler->sendResponse(methodId, result);}});}private:QTcpServer *m_server;QHash<quint32, std::function<QByteArray(const QByteArray&)>> m_methodHandlers;
};
3. 客戶端實現
class RpcClient : public QObject {Q_OBJECT
public:explicit RpcClient(QObject *parent = nullptr): QObject(parent), m_socket(new QTcpSocket(this)), m_handler(new RpcMessageHandler(m_socket, this)) {connect(m_handler, &RpcMessageHandler::responseReceived, this, &RpcClient::onResponseReceived);}void connectToServer(const QString &host, quint16 port) {m_socket->connectToHost(host, port);}void callMethod(quint32 methodId, const QByteArray &params) {m_handler->sendMethodCall(methodId, params);}signals:void methodResponse(quint32 methodId, const QByteArray &result);private slots:void onResponseReceived(quint32 messageId, const QByteArray &result) {emit methodResponse(messageId, result);}private:QTcpSocket *m_socket;RpcMessageHandler *m_handler;
};

三、基于 JSON-RPC 的實現

1. JSON-RPC 消息處理
class JsonRpcHandler : public QObject {Q_OBJECT
public:explicit JsonRpcHandler(QIODevice *device, QObject *parent = nullptr): QObject(parent), m_device(device) {connect(m_device, &QIODevice::readyRead, this, &JsonRpcHandler::onReadyRead);}signals:void methodCallReceived(const QString &method, const QJsonValue &params, const QJsonValue &id);void notificationReceived(const QString &method, const QJsonValue &params);public slots:void sendResponse(const QJsonValue &result, const QJsonValue &id) {QJsonObject response;response["jsonrpc"] = "2.0";response["result"] = result;response["id"] = id;sendMessage(response);}void sendError(const QString &message, int code, const QJsonValue &id) {QJsonObject error;error["code"] = code;error["message"] = message;QJsonObject response;response["jsonrpc"] = "2.0";response["error"] = error;response["id"] = id;sendMessage(response);}private slots:void onReadyRead() {// 讀取完整的 JSON 消息while (m_device->bytesAvailable() > 0) {m_buffer.append(m_device->readAll());// 簡單解析:假設消息以換行符分隔while (true) {int newlinePos = m_buffer.indexOf('\n');if (newlinePos == -1) break;QByteArray message = m_buffer.left(newlinePos);m_buffer = m_buffer.mid(newlinePos + 1);processMessage(message);}}}private:void processMessage(const QByteArray &message) {QJsonParseError error;QJsonDocument doc = QJsonDocument::fromJson(message, &error);if (error.error != QJsonParseError::NoError) {qDebug() << "JSON parse error:" << error.errorString();return;}if (!doc.isObject()) {qDebug() << "Invalid JSON-RPC message (not an object)";return;}QJsonObject obj = doc.object();// 驗證版本if (obj["jsonrpc"].toString() != "2.0") {qDebug() << "Invalid JSON-RPC version";return;}// 檢查是請求還是通知if (obj.contains("method")) {QString method = obj["method"].toString();if (obj.contains("id")) {// 這是一個方法調用QJsonValue params = obj["params"];QJsonValue id = obj["id"];emit methodCallReceived(method, params, id);} else {// 這是一個通知QJsonValue params = obj["params"];emit notificationReceived(method, params);}}}void sendMessage(const QJsonObject &message) {QJsonDocument doc(message);QByteArray data = doc.toJson(QJsonDocument::Compact) + '\n';m_device->write(data);}private:QIODevice *m_device;QByteArray m_buffer;
};
2. JSON-RPC 服務端
class JsonRpcServer : public QObject {Q_OBJECT
public:explicit JsonRpcServer(quint16 port, QObject *parent = nullptr): QObject(parent), m_server(new QTcpServer(this)) {connect(m_server, &QTcpServer::newConnection, this, &JsonRpcServer::onNewConnection);if (!m_server->listen(QHostAddress::Any, port)) {qDebug() << "Server could not start!";} else {qDebug() << "Server started!";}}void registerMethod(const QString &method, std::function<QJsonValue(const QJsonValue&)> handler) {m_methodHandlers[method] = handler;}private slots:void onNewConnection() {QTcpSocket *socket = m_server->nextPendingConnection();JsonRpcHandler *handler = new JsonRpcHandler(socket, this);connect(handler, &JsonRpcHandler::methodCallReceived, this, [this, handler](const QString &method, const QJsonValue &params, const QJsonValue &id) {if (m_methodHandlers.contains(method)) {QJsonValue result = m_methodHandlers[method](params);handler->sendResponse(result, id);} else {handler->sendError("Method not found", -32601, id);}});}private:QTcpServer *m_server;QHash<QString, std::function<QJsonValue(const QJsonValue&)>> m_methodHandlers;
};
3. JSON-RPC 客戶端
class JsonRpcClient : public QObject {Q_OBJECT
public:explicit JsonRpcClient(QObject *parent = nullptr): QObject(parent), m_socket(new QTcpSocket(this)), m_handler(new JsonRpcHandler(m_socket, this)) {connect(m_handler, &JsonRpcHandler::methodCallReceived, this, &JsonRpcClient::onMethodCallReceived);connect(m_handler, &JsonRpcHandler::notificationReceived, this, &JsonRpcClient::onNotificationReceived);}void connectToServer(const QString &host, quint16 port) {m_socket->connectToHost(host, port);}void callMethod(const QString &method, const QJsonValue &params = QJsonValue()) {static quint64 nextId = 1;QJsonObject request;request["jsonrpc"] = "2.0";request["method"] = method;request["params"] = params;request["id"] = QString::number(nextId++);m_handler->sendMessage(request);}signals:void methodCallReceived(const QString &method, const QJsonValue &params, const QJsonValue &id);void notificationReceived(const QString &method, const QJsonValue &params);private slots:void onMethodCallReceived(const QString &method, const QJsonValue &params, const QJsonValue &id) {// 處理方法調用(對于客戶端來說通常不需要)}void onNotificationReceived(const QString &method, const QJsonValue &params) {// 處理通知}private:QTcpSocket *m_socket;JsonRpcHandler *m_handler;
};

四、第三方 RPC 庫集成

1. 使用 gRPC
// 首先需要使用 .proto 文件定義服務
// example.proto
syntax = "proto3";package example;service MyService {rpc GetData (DataRequest) returns (DataResponse);rpc StreamData (stream DataRequest) returns (stream DataResponse);
}message DataRequest {string query = 1;
}message DataResponse {string result = 1;
}// 然后使用 protoc 和 grpc_cpp_plugin 生成代碼
// 最后實現服務和客戶端
2. gRPC 服務端實現
#include <grpcpp/grpcpp.h>
#include "example.grpc.pb.h"using grpc::Server;
using grpc::ServerBuilder;
using grpc::ServerContext;
using grpc::Status;
using example::MyService;
using example::DataRequest;
using example::DataResponse;// 實現服務
class MyServiceImpl final : public MyService::Service {Status GetData(ServerContext* context, const DataRequest* request,DataResponse* response) override {std::string prefix("Hello ");response->set_result(prefix + request->query());return Status::OK();}
};void RunServer() {std::string server_address("0.0.0.0:50051");MyServiceImpl service;ServerBuilder builder;builder.AddListeningPort(server_address, grpc::InsecureServerCredentials());builder.RegisterService(&service);std::unique_ptr<Server> server(builder.BuildAndStart());std::cout << "Server listening on " << server_address << std::endl;server->Wait();
}int main(int argc, char** argv) {RunServer();return 0;
}
3. gRPC 客戶端實現
#include <grpcpp/grpcpp.h>
#include "example.grpc.pb.h"using grpc::Channel;
using grpc::ClientContext;
using grpc::Status;
using example::MyService;
using example::DataRequest;
using example::DataResponse;class MyServiceClient {
public:MyServiceClient(std::shared_ptr<Channel> channel): stub_(MyService::NewStub(channel)) {}std::string GetData(const std::string& user) {DataRequest request;request.set_query(user);DataResponse response;ClientContext context;Status status = stub_->GetData(&context, request, &response);if (status.ok()) {return response.result();} else {std::cout << status.error_code() << ": " << status.error_message()<< std::endl;return "RPC failed";}}private:std::unique_ptr<MyService::Stub> stub_;
};int main(int argc, char** argv) {MyServiceClient greeter(grpc::CreateChannel("localhost:50051", grpc::InsecureChannelCredentials()));std::string user("world");std::string reply = greeter.GetData(user);std::cout << "Greeter received: " << reply << std::endl;return 0;
}

五、RPC 性能優化

1. 連接池管理
class RpcConnectionPool : public QObject {Q_OBJECT
public:explicit RpcConnectionPool(const QString &host, quint16 port, int maxConnections = 10, QObject *parent = nullptr): QObject(parent), m_host(host), m_port(port), m_maxConnections(maxConnections) {// 預創建一些連接for (int i = 0; i < qMin(3, maxConnections); ++i) {createNewConnection();}}QIODevice* acquireConnection() {// 從空閑連接中獲取if (!m_idleConnections.isEmpty()) {QIODevice *connection = m_idleConnections.takeFirst();m_activeConnections.append(connection);return connection;}// 如果沒有空閑連接且未達到最大連接數,則創建新連接if (m_activeConnections.size() + m_idleConnections.size() < m_maxConnections) {return createNewConnection();}// 達到最大連接數,等待連接釋放return nullptr;  // 實際實現中應該等待信號}void releaseConnection(QIODevice *connection) {m_activeConnections.removeAll(connection);m_idleConnections.append(connection);}private:QIODevice* createNewConnection() {QTcpSocket *socket = new QTcpSocket(this);socket->connectToHost(m_host, m_port);if (socket->waitForConnected()) {m_idleConnections.append(socket);return socket;} else {delete socket;return nullptr;}}private:QString m_host;quint16 m_port;int m_maxConnections;QList<QIODevice*> m_idleConnections;QList<QIODevice*> m_activeConnections;
};
2. 異步處理
// 異步 RPC 調用
QFuture<QByteArray> asyncCall(RpcClient *client, quint32 methodId, const QByteArray &params) {return QtConcurrent::run([client, methodId, params]() {QEventLoop loop;QByteArray result;// 連接信號QMetaObject::Connection conn = QObject::connect(client, &RpcClient::methodResponse, [&](quint32 id, const QByteArray &data) {if (id == methodId) {result = data;loop.quit();}});// 發送請求client->callMethod(methodId, params);// 等待響應loop.exec();// 斷開連接QObject::disconnect(conn);return result;});
}

六、RPC 安全機制

1. 基于 SSL/TLS 的安全通信
// 配置安全的 RPC 連接
void setupSecureRpcConnection(QTcpSocket *socket) {// 啟用 SSLQSslSocket *sslSocket = qobject_cast<QSslSocket*>(socket);if (!sslSocket) {sslSocket = new QSslSocket(socket->parent());// 復制連接參數// ...}// 配置 SSLQSslConfiguration config = QSslConfiguration::defaultConfiguration();config.setProtocol(QSsl::TlsV1_3);// 加載證書QSslCertificate cert(":/certs/server-cert.pem");QSslKey key(":/certs/server-key.pem", QSsl::Rsa, QSsl::Pem, QSsl::PrivateKey, "password");if (!cert.isNull() && !key.isNull()) {config.setLocalCertificate(cert);config.setPrivateKey(key);}// 設置 CA 證書QList<QSslCertificate> caCerts = QSslCertificate::fromPath(":/certs/ca-cert.pem");if (!caCerts.isEmpty()) {config.addCaCertificates(caCerts);}// 應用配置sslSocket->setSslConfiguration(config);// 連接信號connect(sslSocket, &QSslSocket::sslErrors, [](const QList<QSslError> &errors) {qDebug() << "SSL errors:";foreach (const QSslError &error, errors) {qDebug() << error.errorString();}// 可以選擇忽略特定錯誤// sslSocket->ignoreSslErrors();});// 啟動加密連接sslSocket->startClientEncryption();
}
2. 身份驗證與授權
// 實現簡單的身份驗證
class RpcAuthenticator : public QObject {Q_OBJECT
public:explicit RpcAuthenticator(QObject *parent = nullptr) : QObject(parent) {}bool authenticate(const QString &username, const QString &password) {// 實際應用中應該從數據庫或配置文件中驗證return (username == "admin" && password == "secret");}bool authorize(const QString &username, const QString &method) {// 實現基于角色的訪問控制if (username == "admin") {return true;  // 管理員可以訪問所有方法} else if (method.startsWith("read")) {return true;  // 普通用戶可以訪問讀方法}return false;}
};

七、總結

Qt 提供了多種 RPC 實現方案,每種方案都有其適用場景:

  1. Qt Remote Objects:官方框架,適合 Qt 內部跨進程/網絡通信,基于信號槽,使用簡單
  2. 自定義協議 RPC:靈活但需要自行實現協議,適合對性能要求高、協議簡單的場景
  3. JSON-RPC:跨語言兼容,協議簡單,適合輕量級服務
  4. gRPC:高性能、跨語言,適合大型分布式系統

在選擇 RPC 方案時,需要考慮以下因素:

  • 性能需求
  • 跨平臺/跨語言需求
  • 安全性要求
  • 開發復雜度
  • 生態系統支持

無論選擇哪種方案,都應關注連接管理、異步處理、安全認證等方面的優化,以構建高效、可靠、安全的分布式系統。

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

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

相關文章

攻防世界-引導-Web_php_unserialize

題目內容&#xff1a;出現一段源代碼&#xff0c;分段分析第一部分如下<?php class Demo { private $file index.php;public function __construct($file) { $this->file $file; }function __destruct() { echo highlight_file($this->file, true); }function __w…

pytorch學習筆記-自定義卷積

未完結的草稿———&#xff01;大概是準備整合一下常見的層&#xff0c;整合完感覺就可以進行搭建了&#xff08;還沒進行到這一步所以不太確定版&#xff09; &#xff08;ps我將在完結這一篇的時候刪除上面的小字and二編一下整篇文章的結構&#xff0c;如果看到了這部分文字…

[明道云]-基礎教學2-工作表字段 vs 控件:選哪種?

本文深入解析“工作表字段”與“控件”的關系與差別,并從結構、功能、使用場景和選型建議等方面進行對比。 一、基礎概念厘清 ? 工作表字段 = 數據模型中的列 工作表字段相當于數據庫表中的列,是記錄每條業務對象(如訂單、客戶等)屬性的數據項,每個字段都有明確的名稱和…

C++-一篇文章入門coroutines協程

文章目錄前言什么是協程協程實現原理C協程的最小例子12345協程等效代碼協程傳值的例子前言 最近學習了一下C協程&#xff0c;這篇文章將介紹協程的相關概念&#xff0c;以及在C中如何使用協程。 什么是協程 C中&#xff0c;協程&#xff08;coroutines&#xff09;可以理解為…

數字經濟專業的就業全景指南

CDA數據分析師證書含金量高&#xff0c;適應了未來數字化經濟和AI發展趨勢&#xff0c;難度不高&#xff0c;行業認可度高&#xff0c;對于找工作很有幫助。一、數字經濟就業熱力圖二、核心崗位發展路徑1. 互聯網數字運營崗2. 金融科技崗崗位類型技能組合證書加持5年薪資范圍智…

PDF轉Word免費工具!批量處理PDF壓縮,合并, OCR識別, 去水印, 簽名等全功能詳解

大家好&#xff0c;歡迎來到程序視點&#xff01;我是你們的老朋友.小二&#xff01;前言PDF軟件我發的非常多&#xff0c;但今天這款工具是大家公認最值得推薦的&#xff0c;這款軟件就是PDF24PDF24幾乎包含了PDF的所有功能&#xff0c;目前是更新到了最新版本&#xff01;文末…

Flutter開發實戰之Widget體系與布局原理

第3章:Widget體系與布局原理 在前面兩章中,我們已經搭建好了Flutter開發環境,并且了解了Dart語言的基礎知識。現在是時候深入Flutter的核心——Widget體系了。如果說Dart是Flutter的語言基礎,那么Widget就是Flutter的靈魂。理解Widget體系,是掌握Flutter開發的關鍵所在。…

C++:stack與queue的使用

stack與queue的使用一.stack與queuej基礎1.stack1.1基本認識1.2示例代碼代碼功能解析2.queue2.1基礎知識操作說明2.2示例代碼代碼分析 一.stack與queuej基礎 1.stack 1.1基本認識以上圖片展示了棧&#xff08;stack&#xff09;這種數據結構的基本操作示意。棧是一種遵循后進先…

Unity 編輯器開發 之 Excel導表工具

一個簡單的Excel導表工具&#xff0c;可以用來熱更數據配置工具使用&#xff1a;&#xfeff;&#xfeff;執行菜單 SDGSupporter/Excel/1.Excel2Cs 生成c#腳本。&#xfeff;&#xfeff;等待C#類編譯完成&#xfeff;&#xfeff;執行菜單 SDGSupporter/Excel/2.Excel2Bytes …

【數據結構與算法】力扣 415. 字符串相加

題目描述 415. 字符串相加 給定兩個字符串形式的非負整數 num1 和num2 &#xff0c;計算它們的和并同樣以字符串形式返回。 你不能使用任何內建的用于處理大整數的庫&#xff08;比如 BigInteger&#xff09;&#xff0c; 也不能直接將輸入的字符串轉換為整數形式。 示例 1…

進階向:Manus AI與多語言手寫識別

Manus AI與多語言手寫識別:從零開始理解 手寫識別技術作為人工智能領域的重要應用之一,近年來在智能設備、教育、金融等行業得到了廣泛運用。根據市場調研機構IDC的數據顯示,2022年全球手寫識別市場規模已達到45億美元,預計到2025年將突破70億美元。其中,多語言手寫識別技…

Javaweb————HTTP請求頭屬性講解

??????????????????????前面我們已經說過http請求分為三部分&#xff0c;請求行&#xff0c;請求頭和請求體 請求頭包含若干個屬性&#xff1a;格式為屬性名&#xff1a;屬性值&#xff0c;這篇文章我們就來介紹一下http請求頭中一些常見屬性的含義 我們…

9.c語言常用算法

查找順序查找&#xff08;線性查找&#xff09;算法思想&#xff1a;從數組的第一個元素開始&#xff0c;逐個與目標值進行比較&#xff0c;直到找到目標值或查找完整個數組。時間復雜度&#xff1a;最好情況&#xff1a;O(1)&#xff08;目標在第一個位置&#xff09;最壞情況…

AI小智源碼分析——音頻部分(一)

一、源碼跳轉這里采用了函數重載來進行代碼復用&#xff0c;當需要對I2S接口的數據進行配置&#xff0c;比如左右音道切換&#xff0c;可以使用第二個構造函數&#xff0c;這里小智使用的是第一個構造函數&#xff0c;即只傳遞I2S相關的引腳參數&#xff08;不帶slot mask&…

【GNSS原理】【LAMBDA】Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法[2025年7月]

Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法 作者&#xff1a;齊花Guyc(CAUC) 文章目錄Chapter.12 GNSS定位算法——模糊度固定LAMBDA算法一.整周模糊度理論1.LAMBDA算法干了一件什么事情&#xff1f;2.LAMBDA算法步驟&#xff08;1&#xff09;去相關&#xff08;Z變換…

計算機畢業設計java在線二手系統的設計與實現 基于Java的在線二手交易平臺開發 Java技術驅動的二手物品管理系統

計算機畢業設計java在線二手系統的設計與實現z2n189&#xff08;配套有源碼 程序 mysql數據庫 論文&#xff09; 本套源碼可以在文本聯xi,先看具體系統功能演示視頻領取&#xff0c;可分享源碼參考。隨著互聯網技術的飛速發展&#xff0c;二手交易市場也逐漸從傳統的線下模式轉…

如何進行項目復盤?核心要點分析

進行項目復盤需要明確復盤目標、確定復盤參與人員、選擇合適的復盤方法、梳理項目過程與關鍵節點、分析成功與失敗的原因、總結經驗教訓并制定改進計劃。其中&#xff0c;選擇合適的復盤方法尤其關鍵&#xff0c;常見的復盤方法包括魚骨圖分析法、SWOT分析法、PDCA循環法&#…

LeetCode 923.多重三數之和

給定一個整數數組 arr &#xff0c;以及一個整數 target 作為目標值&#xff0c;返回滿足 i < j < k 且 arr[i] arr[j] arr[k] target 的元組 i, j, k 的數量。 由于結果會非常大&#xff0c;請返回 109 7 的模。 示例 1&#xff1a; 輸入&#xff1a;arr [1,1,2,2,…

.Net日志系統Logging-五

日志概念 日志級別 NET (Microsoft.Extensions.Logging) 中定義的 6 個標準日志級別&#xff0c;按嚴重性從低到高排列&#xff1a; 日志級別數值描述典型使用場景Trace0最詳細的信息&#xff0c;包含敏感數據&#xff08;如請求體、密碼哈希等&#xff09;。僅在開發或深度故…

中國貿促會融媒體中心出海活動負責人、出海星球創始人蒞臨綠算技術

近日&#xff0c;中國貿促會融媒體中心出海活動負責人、出海星球創始人王思諾一行蒞臨廣東省綠算技術有限公司&#xff0c;深入考察其核心技術產品與全球化布局。雙方圍繞綠算技術全棧產品體系、創新出海模式及生態共建展開深度對話。綠算技術作為國內智算基礎設施領域的領軍企…