From 86360dbf9730692235d947381b5bc81e2364c4c1 Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Thu, 4 Sep 2025 00:32:43 +0800 Subject: [PATCH 1/2] =?UTF-8?q?feat(jsonrpc):=20=E5=B0=86Rpc=E6=8B=86?= =?UTF-8?q?=E5=88=86=E6=88=90IntIdRpc,StrIdRpc=EF=BC=8C=E5=B9=B6=E9=80=9A?= =?UTF-8?q?=E8=BF=87=E6=B5=8B=E8=AF=95?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit --- modules/jsonrpc/CMakeLists.txt | 10 +- modules/jsonrpc/Makefile | 9 +- modules/jsonrpc/{rpc.cpp => int_id_rpc.cpp} | 44 ++-- modules/jsonrpc/{rpc.h => int_id_rpc.h} | 41 ++-- .../{rpc_test.cpp => int_id_rpc_test.cpp} | 22 +- modules/jsonrpc/proto.cpp | 166 +++++++++++--- modules/jsonrpc/proto.h | 26 ++- modules/jsonrpc/str_id_rpc.cpp | 206 ++++++++++++++++++ modules/jsonrpc/str_id_rpc.h | 107 +++++++++ modules/jsonrpc/str_id_rpc_test.cpp | 204 +++++++++++++++++ modules/util/CMakeLists.txt | 2 + modules/util/Makefile | 2 + modules/util/uuid.cpp | 46 ++++ modules/util/uuid.h | 40 ++++ 14 files changed, 836 insertions(+), 89 deletions(-) rename modules/jsonrpc/{rpc.cpp => int_id_rpc.cpp} (73%) rename modules/jsonrpc/{rpc.h => int_id_rpc.h} (69%) rename modules/jsonrpc/{rpc_test.cpp => int_id_rpc_test.cpp} (92%) create mode 100644 modules/jsonrpc/str_id_rpc.cpp create mode 100644 modules/jsonrpc/str_id_rpc.h create mode 100644 modules/jsonrpc/str_id_rpc_test.cpp create mode 100644 modules/util/uuid.cpp create mode 100644 modules/util/uuid.h diff --git a/modules/jsonrpc/CMakeLists.txt b/modules/jsonrpc/CMakeLists.txt index 7a0aa8f..e2727cf 100644 --- a/modules/jsonrpc/CMakeLists.txt +++ b/modules/jsonrpc/CMakeLists.txt @@ -32,14 +32,18 @@ set(TBOX_JSONRPC_SOURCES protos/raw_stream_proto.cpp protos/header_stream_proto.cpp protos/packet_proto.cpp - rpc.cpp) + int_id_rpc.cpp + str_id_rpc.cpp +) set(TBOX_JSONRPC_TEST_SOURCES ${TBOX_JSONRPC_SOURCES} protos/raw_stream_proto_test.cpp protos/header_stream_proto_test.cpp protos/packet_proto_test.cpp - rpc_test.cpp) + int_id_rpc_test.cpp + str_id_rpc_test.cpp +) add_library(${TBOX_LIBRARY_NAME} ${TBOX_BUILD_LIB_TYPE} ${TBOX_JSONRPC_SOURCES}) add_library(tbox::${TBOX_LIBRARY_NAME} ALIAS ${TBOX_LIBRARY_NAME}) @@ -69,7 +73,7 @@ install( # install header file install( - FILES proto.h rpc.h + FILES proto.h int_id_rpc.h str_id_rpc.h DESTINATION ${CMAKE_INSTALL_INCLUDEDIR}/tbox/jsonrpc ) diff --git a/modules/jsonrpc/Makefile b/modules/jsonrpc/Makefile index 1a3d71d..5183519 100644 --- a/modules/jsonrpc/Makefile +++ b/modules/jsonrpc/Makefile @@ -29,14 +29,16 @@ HEAD_FILES = \ protos/raw_stream_proto.h \ protos/header_stream_proto.h \ protos/packet_proto.h \ - rpc.h \ + int_id_rpc.h \ + str_id_rpc.h \ CPP_SRC_FILES = \ proto.cpp \ protos/raw_stream_proto.cpp \ protos/header_stream_proto.cpp \ protos/packet_proto.cpp \ - rpc.cpp \ + int_id_rpc.cpp \ + str_id_rpc.cpp \ CXXFLAGS := -DMODULE_ID='"tbox.jsonrpc"' $(CXXFLAGS) @@ -45,7 +47,8 @@ TEST_CPP_SRC_FILES = \ protos/raw_stream_proto_test.cpp \ protos/header_stream_proto_test.cpp \ protos/packet_proto_test.cpp \ - rpc_test.cpp \ + int_id_rpc_test.cpp \ + str_id_rpc_test.cpp \ TEST_LDFLAGS := $(LDFLAGS) -ltbox_event -ltbox_util -ltbox_base -ldl ENABLE_SHARED_LIB = no diff --git a/modules/jsonrpc/rpc.cpp b/modules/jsonrpc/int_id_rpc.cpp similarity index 73% rename from modules/jsonrpc/rpc.cpp rename to modules/jsonrpc/int_id_rpc.cpp index f334b7a..89e675c 100644 --- a/modules/jsonrpc/rpc.cpp +++ b/modules/jsonrpc/int_id_rpc.cpp @@ -17,7 +17,7 @@ * project authors may be found in the CONTRIBUTORS.md file in the root * of the source tree. */ -#include "rpc.h" +#include "int_id_rpc.h" #include #include @@ -28,22 +28,22 @@ namespace tbox { namespace jsonrpc { -Rpc::Rpc(event::Loop *loop) +IntIdRpc::IntIdRpc(event::Loop *loop) : request_timeout_(loop) , respond_timeout_(loop) { using namespace std::placeholders; - request_timeout_.setCallback(std::bind(&Rpc::onRequestTimeout, this, _1)); - respond_timeout_.setCallback(std::bind(&Rpc::onRespondTimeout, this, _1)); + request_timeout_.setCallback(std::bind(&IntIdRpc::onRequestTimeout, this, _1)); + respond_timeout_.setCallback(std::bind(&IntIdRpc::onRespondTimeout, this, _1)); } -Rpc::~Rpc() +IntIdRpc::~IntIdRpc() { respond_timeout_.cleanup(); request_timeout_.cleanup(); } -bool Rpc::initialize(Proto *proto, int timeout_sec) +bool IntIdRpc::initialize(Proto *proto, int timeout_sec) { using namespace std::placeholders; @@ -51,15 +51,15 @@ bool Rpc::initialize(Proto *proto, int timeout_sec) respond_timeout_.initialize(std::chrono::seconds(1), timeout_sec); proto->setRecvCallback( - std::bind(&Rpc::onRecvRequest, this, _1, _2, _3), - std::bind(&Rpc::onRecvRespond, this, _1, _2, _3) + std::bind(&IntIdRpc::onRecvRequest, this, _1, _2, _3), + std::bind(&IntIdRpc::onRecvRespond, this, _1, _2, _3) ); proto_ = proto; return true; } -void Rpc::cleanup() +void IntIdRpc::cleanup() { respond_timeout_.cleanup(); request_timeout_.cleanup(); @@ -69,11 +69,11 @@ void Rpc::cleanup() method_services_.clear(); - proto_->setRecvCallback(nullptr, nullptr); + proto_->cleanup(); proto_ = nullptr; } -void Rpc::request(const std::string &method, const Json &js_params, RequestCallback &&cb) +void IntIdRpc::request(const std::string &method, const Json &js_params, RequestCallback &&cb) { RECORD_SCOPE(); int id = 0; @@ -85,27 +85,27 @@ void Rpc::request(const std::string &method, const Json &js_params, RequestCallb proto_->sendRequest(id, method, js_params); } -void Rpc::request(const std::string &method, RequestCallback &&cb) +void IntIdRpc::request(const std::string &method, RequestCallback &&cb) { request(method, Json(), std::move(cb)); } -void Rpc::notify(const std::string &method, const Json &js_params) +void IntIdRpc::notify(const std::string &method, const Json &js_params) { request(method, js_params, nullptr); } -void Rpc::notify(const std::string &method) +void IntIdRpc::notify(const std::string &method) { request(method, Json(), nullptr); } -void Rpc::addService(const std::string &method, ServiceCallback &&cb) +void IntIdRpc::addService(const std::string &method, ServiceCallback &&cb) { method_services_[method] = std::move(cb); } -void Rpc::respond(int id, int errcode, const Json &js_result) +void IntIdRpc::respond(IdTypeConstRef id, int errcode, const Json &js_result) { RECORD_SCOPE(); if (id == 0) { @@ -122,7 +122,7 @@ void Rpc::respond(int id, int errcode, const Json &js_result) tobe_respond_.erase(id); } -void Rpc::respond(int id, const Json &js_result) +void IntIdRpc::respond(IdTypeConstRef id, const Json &js_result) { RECORD_SCOPE(); if (id == 0) { @@ -134,7 +134,7 @@ void Rpc::respond(int id, const Json &js_result) tobe_respond_.erase(id); } -void Rpc::respond(int id, int errcode) +void IntIdRpc::respond(IdTypeConstRef id, int errcode) { RECORD_SCOPE(); if (id == 0) { @@ -146,7 +146,7 @@ void Rpc::respond(int id, int errcode) tobe_respond_.erase(id); } -void Rpc::onRecvRequest(int id, const std::string &method, const Json &js_params) +void IntIdRpc::onRecvRequest(IdTypeConstRef id, const std::string &method, const Json &js_params) { RECORD_SCOPE(); auto iter = method_services_.find(method); @@ -168,7 +168,7 @@ void Rpc::onRecvRequest(int id, const std::string &method, const Json &js_params } } -void Rpc::onRecvRespond(int id, int errcode, const Json &js_result) +void IntIdRpc::onRecvRespond(IdTypeConstRef id, int errcode, const Json &js_result) { RECORD_SCOPE(); auto iter = request_callback_.find(id); @@ -179,7 +179,7 @@ void Rpc::onRecvRespond(int id, int errcode, const Json &js_result) } } -void Rpc::onRequestTimeout(int id) +void IntIdRpc::onRequestTimeout(IdTypeConstRef id) { auto iter = request_callback_.find(id); if (iter != request_callback_.end()) { @@ -189,7 +189,7 @@ void Rpc::onRequestTimeout(int id) } } -void Rpc::onRespondTimeout(int id) +void IntIdRpc::onRespondTimeout(IdTypeConstRef id) { auto iter = tobe_respond_.find(id); if (iter != tobe_respond_.end()) { diff --git a/modules/jsonrpc/rpc.h b/modules/jsonrpc/int_id_rpc.h similarity index 69% rename from modules/jsonrpc/rpc.h rename to modules/jsonrpc/int_id_rpc.h index 4a11b1e..c69e5b1 100644 --- a/modules/jsonrpc/rpc.h +++ b/modules/jsonrpc/int_id_rpc.h @@ -17,8 +17,8 @@ * project authors may be found in the CONTRIBUTORS.md file in the root * of the source tree. */ -#ifndef TBOX_JSONRPC_RPC_H -#define TBOX_JSONRPC_RPC_H +#ifndef TBOX_JSONRPC_INT_RPC_H +#define TBOX_JSONRPC_INT_RPC_H #include #include @@ -32,8 +32,11 @@ namespace jsonrpc { class Proto; -class Rpc { +class IntIdRpc { public: + using IdType = int; + using IdTypeConstRef = int; + /** * 收到对端回复的回调函数 * @@ -53,11 +56,11 @@ class Rpc { * \return true 同步回复:在函数返回后自动回复,根据errcode与js_result进行回复 * \return false 异步回复:不在函数返回后自动回复,而是在稍候通过调respond()进行回复 */ - using ServiceCallback = std::function; + using ServiceCallback = std::function; public: - explicit Rpc(event::Loop *loop); - virtual ~Rpc(); + explicit IntIdRpc(event::Loop *loop); + virtual ~IntIdRpc(); bool initialize(Proto *proto, int timeout_sec = 30); void cleanup(); @@ -74,29 +77,29 @@ class Rpc { void notify(const std::string &method); //! 发送异步回复 - void respond(int id, int errcode, const Json &js_result); - void respond(int id, const Json &js_result); - void respond(int id, int errcode); + void respond(IdTypeConstRef id, int errcode, const Json &js_result); + void respond(IdTypeConstRef id, const Json &js_result); + void respond(IdTypeConstRef id, int errcode); protected: - void onRecvRequest(int id, const std::string &method, const Json ¶ms); - void onRecvRespond(int id, int errcode, const Json &result); - void onRequestTimeout(int id); - void onRespondTimeout(int id); + void onRecvRequest(IdTypeConstRef id, const std::string &method, const Json ¶ms); + void onRecvRespond(IdTypeConstRef id, int errcode, const Json &result); + void onRequestTimeout(IdTypeConstRef id); + void onRespondTimeout(IdTypeConstRef id); private: Proto *proto_ = nullptr; std::unordered_map method_services_; - int id_alloc_ = 0; - std::unordered_map request_callback_; - std::unordered_set tobe_respond_; - eventx::TimeoutMonitor request_timeout_; //! 请求超时监测 - eventx::TimeoutMonitor respond_timeout_; //! 回复超时监测 + IdType id_alloc_ = 0; + std::unordered_map request_callback_; + std::unordered_set tobe_respond_; + eventx::TimeoutMonitor request_timeout_; //! 请求超时监测 + eventx::TimeoutMonitor respond_timeout_; //! 回复超时监测 }; } } -#endif //TBOX_JSONRPC_RPC_H +#endif //TBOX_JSONRPC_INT_RPC_H diff --git a/modules/jsonrpc/rpc_test.cpp b/modules/jsonrpc/int_id_rpc_test.cpp similarity index 92% rename from modules/jsonrpc/rpc_test.cpp rename to modules/jsonrpc/int_id_rpc_test.cpp index 3ac766b..4facde3 100644 --- a/modules/jsonrpc/rpc_test.cpp +++ b/modules/jsonrpc/int_id_rpc_test.cpp @@ -25,20 +25,20 @@ #include #include "protos/raw_stream_proto.h" -#include "rpc.h" +#include "int_id_rpc.h" namespace tbox { namespace jsonrpc { -class RpcTest : public testing::Test { +class IntIdRpcTest : public testing::Test { protected: event::Loop *loop; //! 生成两个端,让它们通信 - Rpc rpc_a, rpc_b; + IntIdRpc rpc_a, rpc_b; RawStreamProto proto_a, proto_b; public: - RpcTest() + IntIdRpcTest() : loop(event::Loop::New()) , rpc_a(loop) , rpc_b(loop) @@ -46,7 +46,7 @@ class RpcTest : public testing::Test { LogOutput_Enable(); } - ~RpcTest() { + ~IntIdRpcTest() { delete loop; LogOutput_Disable(); } @@ -76,7 +76,7 @@ class RpcTest : public testing::Test { } }; -TEST_F(RpcTest, SendRequestNormally) { +TEST_F(IntIdRpcTest, SendRequestNormally) { Json js_req_params = { {"a", 12}, {"b", "test jsonrpc"} }; Json js_rsp_result = { {"r", "aabbcc"} }; @@ -111,7 +111,7 @@ TEST_F(RpcTest, SendRequestNormally) { EXPECT_TRUE(is_method_cb_invoke); } -TEST_F(RpcTest, SendMessageNormally) { +TEST_F(IntIdRpcTest, SendMessageNormally) { Json js_req_params = { {"a", 12}, {"b", "test jsonrpc"} }; bool is_service_invoke = false; @@ -135,7 +135,7 @@ TEST_F(RpcTest, SendMessageNormally) { EXPECT_TRUE(is_service_invoke); } -TEST_F(RpcTest, SendMessageNoService) { +TEST_F(IntIdRpcTest, SendMessageNoService) { bool is_service_invoke = false; rpc_b.addService("B", [&] (int id, const Json &, int &, Json &) { @@ -156,7 +156,7 @@ TEST_F(RpcTest, SendMessageNoService) { EXPECT_FALSE(is_service_invoke); } -TEST_F(RpcTest, SendRequestNoMethod) { +TEST_F(IntIdRpcTest, SendRequestNoMethod) { bool is_method_cb_invoke = false; loop->run( [&] { @@ -175,11 +175,11 @@ TEST_F(RpcTest, SendRequestNoMethod) { EXPECT_TRUE(is_method_cb_invoke); } -TEST(Rpc, RequestTimeout) { +TEST(IntIdRpc, RequestTimeout) { auto loop = event::Loop::New(); SetScopeExitAction([=] { delete loop; }); - Rpc rpc(loop); + IntIdRpc rpc(loop); RawStreamProto proto; rpc.initialize(&proto, 1); diff --git a/modules/jsonrpc/proto.cpp b/modules/jsonrpc/proto.cpp index eee16e0..86ba642 100644 --- a/modules/jsonrpc/proto.cpp +++ b/modules/jsonrpc/proto.cpp @@ -18,6 +18,7 @@ * of the source tree. */ #include "proto.h" +#include #include #include #include @@ -41,19 +42,55 @@ void Proto::sendRequest(int id, const std::string &method, const Json &js_params sendJson(js); } +void Proto::sendRequest(const std::string &id, const std::string &method, const Json &js_params) +{ + Json js = { + {"jsonrpc", "2.0"}, + {"method", method} + }; + + if (!id.empty()) + js["id"] = id; + + if (!js_params.is_null()) + js["params"] = js_params; + + sendJson(js); +} + void Proto::sendRequest(int id, const std::string &method) { sendRequest(id, method, Json()); } +void Proto::sendRequest(const std::string &id, const std::string &method) +{ + sendRequest(id, method, Json()); +} + void Proto::sendResult(int id, const Json &js_result) { Json js = { {"jsonrpc", "2.0"}, - {"id", id}, {"result", js_result} }; + if (id != 0) + js["id"] = id; + + sendJson(js); +} + +void Proto::sendResult(const std::string &id, const Json &js_result) +{ + Json js = { + {"jsonrpc", "2.0"}, + {"result", js_result} + }; + + if (!id.empty()) + js["id"] = id; + sendJson(js); } @@ -61,20 +98,56 @@ void Proto::sendError(int id, int errcode, const std::string &message) { Json js = { {"jsonrpc", "2.0"}, - {"id", id}, {"error", { {"code", errcode } } } }; + if (id != 0) + js["id"] = id; + + if (!message.empty()) + js["error"]["message"] = message; + + sendJson(js); +} + +void Proto::sendError(const std::string &id, int errcode, const std::string &message) +{ + Json js = { + {"jsonrpc", "2.0"}, + {"error", { {"code", errcode } } } + }; + + if (!id.empty()) + js["id"] = id; + if (!message.empty()) js["error"]["message"] = message; sendJson(js); } -void Proto::setRecvCallback(RecvRequestCallback &&req_cb, RecvRespondCallback &&rsp_cb) +void Proto::setRecvCallback(RecvRequestIntCallback &&req_cb, RecvRespondIntCallback &&rsp_cb) { - recv_request_cb_ = std::move(req_cb); - recv_respond_cb_ = std::move(rsp_cb); + if (req_cb && rsp_cb) { + id_type_ = IdType::kInt; + recv_request_int_cb_ = std::move(req_cb); + recv_respond_int_cb_ = std::move(rsp_cb); + + } else { + LogWarn("int req_cb or rsp_cb is nullptr"); + } +} + +void Proto::setRecvCallback(RecvRequestStrCallback &&req_cb, RecvRespondStrCallback &&rsp_cb) +{ + if (req_cb && rsp_cb) { + id_type_ = IdType::kString; + recv_request_str_cb_ = std::move(req_cb); + recv_respond_str_cb_ = std::move(rsp_cb); + + } else { + LogWarn("str req_cb or rsp_cb is nullptr"); + } } void Proto::setSendCallback(SendDataCallback &&cb) @@ -82,6 +155,15 @@ void Proto::setSendCallback(SendDataCallback &&cb) send_data_cb_ = std::move(cb); } +void Proto::cleanup() +{ + recv_request_int_cb_ = nullptr; + recv_respond_int_cb_ = nullptr; + recv_request_str_cb_ = nullptr; + recv_respond_str_cb_ = nullptr; + send_data_cb_ = nullptr; +} + void Proto::onRecvJson(const Json &js) { if (js.is_object()) { @@ -93,39 +175,55 @@ void Proto::onRecvJson(const Json &js) } if (js.contains("method")) { - if (!recv_request_cb_) - return; - //! 按请求进行处理 std::string method; - int id = 0; if (!util::json::GetField(js, "method", method)) { LogNotice("method type not string"); return; } - util::json::GetField(js, "id", id); - recv_request_cb_(id, method, js.contains("params") ? js["params"] : Json()); + + auto &js_params = js.contains("params") ? js["params"] : Json(); + + if (id_type_ == IdType::kInt) { + int id = 0; + util::json::GetField(js, "id", id); + recv_request_int_cb_(id, method, js_params); + + } else if (id_type_ == IdType::kString) { + std::string id; + util::json::GetField(js, "id", id); + recv_request_str_cb_(id, method, js_params); + + } else { + LogWarn("please invoke setRecvCallback() first."); + } } else if (js.contains("result")) { //! 按结果回复进行处理 - if (!recv_respond_cb_) - return; + auto &js_result = js["result"]; - int id = 0; - if (!util::json::GetField(js, "id", id)) { - LogNotice("no method field in respond"); - return; + if (id_type_ == IdType::kInt) { + int id = 0; + if (!util::json::GetField(js, "id", id)) { + LogNotice("no int id field in respond"); + return; + } + recv_respond_int_cb_(id, 0, js_result); + + } else if (id_type_ == IdType::kString) { + std::string id; + if (!util::json::GetField(js, "id", id)) { + LogNotice("no string id field in respond"); + return; + } + recv_respond_str_cb_(id, 0, js_result); + + } else { + LogWarn("please invoke setRecvCallback() first."); } - recv_respond_cb_(id, 0, js["result"]); } else if (js.contains("error")) { //! 按错误回复进行处理 - if (!recv_respond_cb_) - return; - - int id = 0; - util::json::GetField(js, "id", id); - auto &js_error = js["error"]; int errcode = 0; if (!util::json::GetField(js_error, "code", errcode)) { @@ -133,7 +231,25 @@ void Proto::onRecvJson(const Json &js) return; } - recv_respond_cb_(id, errcode, Json()); + if (id_type_ == IdType::kInt) { + int id = 0; + if (!util::json::GetField(js, "id", id)) { + LogNotice("no int id field in respond"); + return; + } + recv_respond_int_cb_(id, errcode, Json()); + + } else if (id_type_ == IdType::kString) { + std::string id; + if (!util::json::GetField(js, "id", id)) { + LogNotice("no string id field in respond"); + return; + } + recv_respond_str_cb_(id, errcode, Json()); + + } else { + LogWarn("please invoke setRecvCallback() first."); + } } else { LogNotice("not jsonrpc format"); diff --git a/modules/jsonrpc/proto.h b/modules/jsonrpc/proto.h index b54c3b4..15fefd2 100644 --- a/modules/jsonrpc/proto.h +++ b/modules/jsonrpc/proto.h @@ -28,12 +28,16 @@ namespace jsonrpc { class Proto { public: - using RecvRequestCallback = std::function; - using RecvRespondCallback = std::function; + using RecvRequestIntCallback = std::function; + using RecvRespondIntCallback = std::function; + using RecvRequestStrCallback = std::function; + using RecvRespondStrCallback = std::function; using SendDataCallback = std::function; - void setRecvCallback(RecvRequestCallback &&req_cb, RecvRespondCallback &&rsp_cb); + void setRecvCallback(RecvRequestIntCallback &&req_cb, RecvRespondIntCallback &&rsp_cb); + void setRecvCallback(RecvRequestStrCallback &&req_cb, RecvRespondStrCallback &&rsp_cb); void setSendCallback(SendDataCallback &&cb); + void cleanup(); void setLogEnable(bool is_enable) { is_log_enabled_ = is_enable; } void setLogLabel(const std::string &log_label) { log_label_ = log_label; } @@ -41,10 +45,14 @@ class Proto { public: void sendRequest(int id, const std::string &method); void sendRequest(int id, const std::string &method, const Json &js_params); - void sendResult(int id, const Json &js_result); void sendError(int id, int errcode, const std::string &message = ""); + void sendRequest(const std::string &id, const std::string &method); + void sendRequest(const std::string &id, const std::string &method, const Json &js_params); + void sendResult(const std::string &id, const Json &js_result); + void sendError(const std::string &id, int errcode, const std::string &message = ""); + public: /** * 当传输层收到数据后调用。该方法进行解包然后进行后续的处理 @@ -52,12 +60,18 @@ class Proto { virtual ssize_t onRecvData(const void *data_ptr, size_t data_size) = 0; protected: + enum class IdType { kNone, kInt, kString }; + virtual void sendJson(const Json &js) = 0; void onRecvJson(const Json &js); - RecvRequestCallback recv_request_cb_; - RecvRespondCallback recv_respond_cb_; + IdType id_type_ = IdType::kNone; + RecvRequestIntCallback recv_request_int_cb_; + RecvRespondIntCallback recv_respond_int_cb_; + RecvRequestStrCallback recv_request_str_cb_; + RecvRespondStrCallback recv_respond_str_cb_; + SendDataCallback send_data_cb_; bool is_log_enabled_ = false; diff --git a/modules/jsonrpc/str_id_rpc.cpp b/modules/jsonrpc/str_id_rpc.cpp new file mode 100644 index 0000000..9489f02 --- /dev/null +++ b/modules/jsonrpc/str_id_rpc.cpp @@ -0,0 +1,206 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "str_id_rpc.h" + +#include +#include +#include +#include + +#include "proto.h" +#include "inner_types.h" + +namespace tbox { +namespace jsonrpc { + +StrIdRpc::StrIdRpc(event::Loop *loop) + : request_timeout_(loop) + , respond_timeout_(loop) +{ + using namespace std::placeholders; + request_timeout_.setCallback(std::bind(&StrIdRpc::onRequestTimeout, this, _1)); + respond_timeout_.setCallback(std::bind(&StrIdRpc::onRespondTimeout, this, _1)); + + id_gen_func_ = [] { return util::GenUUID(); }; +} + +StrIdRpc::~StrIdRpc() +{ + respond_timeout_.cleanup(); + request_timeout_.cleanup(); +} + +bool StrIdRpc::initialize(Proto *proto, int timeout_sec) +{ + using namespace std::placeholders; + + request_timeout_.initialize(std::chrono::seconds(1), timeout_sec); + respond_timeout_.initialize(std::chrono::seconds(1), timeout_sec); + + proto->setRecvCallback( + std::bind(&StrIdRpc::onRecvRequest, this, _1, _2, _3), + std::bind(&StrIdRpc::onRecvRespond, this, _1, _2, _3) + ); + proto_ = proto; + + return true; +} + +void StrIdRpc::cleanup() +{ + respond_timeout_.cleanup(); + request_timeout_.cleanup(); + + tobe_respond_.clear(); + request_callback_.clear(); + + method_services_.clear(); + + proto_->cleanup(); + proto_ = nullptr; +} + +void StrIdRpc::request(const std::string &method, const Json &js_params, RequestCallback &&cb) +{ + RECORD_SCOPE(); + IdType id; + if (cb && id_gen_func_) { + id = id_gen_func_(); + request_callback_[id] = std::move(cb); + request_timeout_.add(id); + } + proto_->sendRequest(id, method, js_params); +} + +void StrIdRpc::request(const std::string &method, RequestCallback &&cb) +{ + request(method, Json(), std::move(cb)); +} + +void StrIdRpc::notify(const std::string &method, const Json &js_params) +{ + request(method, js_params, nullptr); +} + +void StrIdRpc::notify(const std::string &method) +{ + request(method, Json(), nullptr); +} + +void StrIdRpc::addService(const std::string &method, ServiceCallback &&cb) +{ + method_services_[method] = std::move(cb); +} + +void StrIdRpc::respond(IdTypeConstRef id, int errcode, const Json &js_result) +{ + RECORD_SCOPE(); + if (id.empty()) { + LogWarn("send id.empty() respond"); + return; + } + + if (errcode == 0) { + proto_->sendResult(id, js_result); + } else { + proto_->sendError(id, errcode); + } + + tobe_respond_.erase(id); +} + +void StrIdRpc::respond(IdTypeConstRef id, const Json &js_result) +{ + RECORD_SCOPE(); + if (id.empty()) { + LogWarn("send id.empty() respond"); + return; + } + + proto_->sendResult(id, js_result); + tobe_respond_.erase(id); +} + +void StrIdRpc::respond(IdTypeConstRef id, int errcode) +{ + RECORD_SCOPE(); + if (id.empty()) { + LogWarn("send id.empty() respond"); + return; + } + + proto_->sendError(id, errcode); + tobe_respond_.erase(id); +} + +void StrIdRpc::onRecvRequest(IdTypeConstRef id, const std::string &method, const Json &js_params) +{ + RECORD_SCOPE(); + auto iter = method_services_.find(method); + if (iter != method_services_.end() && iter->second) { + int errcode = 0; + Json js_result; + if (!id.empty()) { + tobe_respond_.insert(id); + if (iter->second(id, js_params, errcode, js_result)) { + respond(id, errcode, js_result); + } else { + respond_timeout_.add(id); + } + } else { + iter->second(id, js_params, errcode, js_result); + } + } else { + proto_->sendError(id, ErrorCode::kMethodNotFound); + } +} + +void StrIdRpc::onRecvRespond(IdTypeConstRef id, int errcode, const Json &js_result) +{ + RECORD_SCOPE(); + auto iter = request_callback_.find(id); + if (iter != request_callback_.end()) { + if (iter->second) + iter->second(errcode, js_result); + request_callback_.erase(iter); + } +} + +void StrIdRpc::onRequestTimeout(IdTypeConstRef id) +{ + auto iter = request_callback_.find(id); + if (iter != request_callback_.end()) { + if (iter->second) + iter->second(ErrorCode::kRequestTimeout, Json()); + request_callback_.erase(iter); + } +} + +void StrIdRpc::onRespondTimeout(IdTypeConstRef id) +{ + auto iter = tobe_respond_.find(id); + if (iter != tobe_respond_.end()) { + LogWarn("respond timeout"); //! 仅仅是提示作用 + tobe_respond_.erase(iter); + } +} + +} +} diff --git a/modules/jsonrpc/str_id_rpc.h b/modules/jsonrpc/str_id_rpc.h new file mode 100644 index 0000000..d6bf22d --- /dev/null +++ b/modules/jsonrpc/str_id_rpc.h @@ -0,0 +1,107 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_JSONRPC_STR_RPC_H +#define TBOX_JSONRPC_STR_RPC_H + +#include +#include +#include +#include +#include +#include + +namespace tbox { +namespace jsonrpc { + +class Proto; + +class StrIdRpc { + public: + using IdType = std::string; + using IdTypeConstRef = const std::string&; + + /** + * 收到对端回复的回调函数 + * + * \param errcode 错误码,= 0 表示没有错误 + * \param js_result 回复的结果 + */ + using RequestCallback = std::function; + + /** + * 收到对端请求时的回调函数 + * + * \param id 请求的id(用于异步回复使用) + * \param js_params 请求的参数 + * \param errcode 将要回复的错误码,= 0 表示没有错误(仅同步回复有效) + * \param js_result 将要回复的结果,只有在 errcode == 0 时才会有效(仅同步回复有效) + * + * \return true 同步回复:在函数返回后自动回复,根据errcode与js_result进行回复 + * \return false 异步回复:不在函数返回后自动回复,而是在稍候通过调respond()进行回复 + */ + using ServiceCallback = std::function; + + //! 生成ID的函数 + using IdGenFunc = std::function; + + public: + explicit StrIdRpc(event::Loop *loop); + virtual ~StrIdRpc(); + + bool initialize(Proto *proto, int timeout_sec = 30); + void cleanup(); + + //! 添加方法被调用时的回调函数 + void addService(const std::string &method, ServiceCallback &&cb); + + //! 发送请求(需要回复的) + void request(const std::string &method, const Json &js_params, RequestCallback &&cb); + void request(const std::string &method, RequestCallback &&cb); + + //! 发送通知(不需要回复的) + void notify(const std::string &method, const Json &js_params); + void notify(const std::string &method); + + //! 发送异步回复 + void respond(IdTypeConstRef id, int errcode, const Json &js_result); + void respond(IdTypeConstRef id, const Json &js_result); + void respond(IdTypeConstRef id, int errcode); + + protected: + void onRecvRequest(IdTypeConstRef id, const std::string &method, const Json ¶ms); + void onRecvRespond(IdTypeConstRef id, int errcode, const Json &result); + void onRequestTimeout(IdTypeConstRef id); + void onRespondTimeout(IdTypeConstRef id); + + private: + Proto *proto_ = nullptr; + + IdGenFunc id_gen_func_; + std::unordered_map method_services_; + std::unordered_map request_callback_; + std::unordered_set tobe_respond_; + eventx::TimeoutMonitor request_timeout_; //! 请求超时监测 + eventx::TimeoutMonitor respond_timeout_; //! 回复超时监测 +}; + +} +} + +#endif //TBOX_JSONRPC_STR_RPC_H diff --git a/modules/jsonrpc/str_id_rpc_test.cpp b/modules/jsonrpc/str_id_rpc_test.cpp new file mode 100644 index 0000000..863b481 --- /dev/null +++ b/modules/jsonrpc/str_id_rpc_test.cpp @@ -0,0 +1,204 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include +#include +#include +#include +#include +#include + +#include "protos/raw_stream_proto.h" +#include "str_id_rpc.h" + +namespace tbox { +namespace jsonrpc { + +class StrIdRpcTest : public testing::Test { + protected: + event::Loop *loop; + //! 生成两个端,让它们通信 + StrIdRpc rpc_a, rpc_b; + RawStreamProto proto_a, proto_b; + + public: + StrIdRpcTest() + : loop(event::Loop::New()) + , rpc_a(loop) + , rpc_b(loop) + { + LogOutput_Enable(); + } + + ~StrIdRpcTest() { + delete loop; + LogOutput_Disable(); + } + + void SetUp() override { + rpc_a.initialize(&proto_a); + rpc_b.initialize(&proto_b); + + //! a发的b收 + proto_a.setSendCallback( + [&](const void *data_ptr, size_t data_size) { + proto_b.onRecvData(data_ptr, data_size); + } + ); + //! b发的a收 + proto_b.setSendCallback( + [&](const void *data_ptr, size_t data_size) { + proto_a.onRecvData(data_ptr, data_size); + } + ); + } + + void TearDown() override { + rpc_a.cleanup(); + rpc_b.cleanup(); + loop->cleanup(); + } +}; + +TEST_F(StrIdRpcTest, SendRequestNormally) { + Json js_req_params = { {"a", 12}, {"b", "test jsonrpc"} }; + Json js_rsp_result = { {"r", "aabbcc"} }; + + bool is_service_invoke = false; + rpc_b.addService("A", + [&] (const std::string &id, const Json &js_params, int &errcode, Json &js_result) { + EXPECT_EQ(js_params, js_req_params); + errcode = 0; + js_result = js_rsp_result; + is_service_invoke = true; + UNUSED_VAR(id); + return true; + } + ); + + bool is_method_cb_invoke = false; + loop->run( + [&] { + rpc_a.request("A", js_req_params, + [&] (int errcode, const Json &js_result) { + EXPECT_EQ(errcode, 0); + EXPECT_EQ(js_result, js_rsp_result); + is_method_cb_invoke = true; + } + ); + } + ); + loop->exitLoop(std::chrono::milliseconds(10)); + loop->runLoop(); + + EXPECT_TRUE(is_service_invoke); + EXPECT_TRUE(is_method_cb_invoke); +} + +TEST_F(StrIdRpcTest, SendMessageNormally) { + Json js_req_params = { {"a", 12}, {"b", "test jsonrpc"} }; + + bool is_service_invoke = false; + rpc_b.addService("B", + [&] (const std::string &id, const Json &js_params, int &, Json &) { + EXPECT_TRUE(id.empty()); + EXPECT_EQ(js_params, js_req_params); + is_service_invoke = true; + return true; + } + ); + + loop->run( + [&] { + rpc_a.notify("B", js_req_params); + } + ); + loop->exitLoop(std::chrono::milliseconds(10)); + loop->runLoop(); + + EXPECT_TRUE(is_service_invoke); +} + +TEST_F(StrIdRpcTest, SendMessageNoService) { + bool is_service_invoke = false; + rpc_b.addService("B", + [&] (const std::string &, const Json &, int &, Json &) { + is_service_invoke = true; + return true; + } + ); + + loop->run( + [&] { + rpc_a.notify("A"); + } + ); + loop->exitLoop(std::chrono::milliseconds(10)); + loop->runLoop(); + + EXPECT_FALSE(is_service_invoke); +} + +TEST_F(StrIdRpcTest, SendRequestNoMethod) { + bool is_method_cb_invoke = false; + loop->run( + [&] { + rpc_a.request("A", Json(), + [&] (int errcode, const Json &js_result) { + EXPECT_EQ(errcode, -32601); + EXPECT_EQ(js_result, Json()); + is_method_cb_invoke = true; + } + ); + } + ); + loop->exitLoop(std::chrono::milliseconds(10)); + loop->runLoop(); + + EXPECT_TRUE(is_method_cb_invoke); +} + +TEST(StrIdRpc, RequestTimeout) { + auto loop = event::Loop::New(); + SetScopeExitAction([=] { delete loop; }); + + StrIdRpc rpc(loop); + RawStreamProto proto; + rpc.initialize(&proto, 1); + + bool is_method_cb_invoke = false; + loop->run( + [&] { + rpc.request("A", Json(), + [&] (int errcode, const Json &js_result) { + EXPECT_EQ(errcode, -32000); + UNUSED_VAR(js_result); + is_method_cb_invoke = true; + } + ); + } + ); + loop->exitLoop(std::chrono::milliseconds(1001)); + loop->runLoop(); + + EXPECT_TRUE(is_method_cb_invoke); +} + +} +} diff --git a/modules/util/CMakeLists.txt b/modules/util/CMakeLists.txt index af9e88a..859517b 100644 --- a/modules/util/CMakeLists.txt +++ b/modules/util/CMakeLists.txt @@ -49,6 +49,7 @@ set(TBOX_UTIL_HEADERS scalable_integer.h variables.h string_to.h + uuid.h ) set(TBOX_UTIL_SOURCES @@ -71,6 +72,7 @@ set(TBOX_UTIL_SOURCES scalable_integer.cpp variables.cpp string_to.cpp + uuid.cpp ) set(TBOX_UTIL_TEST_SOURCES diff --git a/modules/util/Makefile b/modules/util/Makefile index daf2087..d6d6cdf 100644 --- a/modules/util/Makefile +++ b/modules/util/Makefile @@ -44,6 +44,7 @@ HEAD_FILES = \ scalable_integer.h \ variables.h \ string_to.h \ + uuid.h \ CPP_SRC_FILES = \ pid_file.cpp \ @@ -65,6 +66,7 @@ CPP_SRC_FILES = \ scalable_integer.cpp \ variables.cpp \ string_to.cpp \ + uuid.cpp \ CXXFLAGS := -DMODULE_ID='"tbox.util"' $(CXXFLAGS) diff --git a/modules/util/uuid.cpp b/modules/util/uuid.cpp new file mode 100644 index 0000000..e1fc879 --- /dev/null +++ b/modules/util/uuid.cpp @@ -0,0 +1,46 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2025 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#include "uuid.h" +#include "fs.h" + +namespace tbox { +namespace util { + +namespace { +std::string _GenByLinuxKernal() +{ + std::string uuid; + fs::ReadFirstLineFromTextFile("/proc/sys/kernel/random/uuid", uuid); + return uuid; +} +} + +std::string GenUUID(UUIDGenStratey stratey) +{ + switch (stratey) { + case UUIDGenStratey::kLinuxKernal: + return _GenByLinuxKernal(); + default: + return ""; + } +} + +} +} diff --git a/modules/util/uuid.h b/modules/util/uuid.h new file mode 100644 index 0000000..851b588 --- /dev/null +++ b/modules/util/uuid.h @@ -0,0 +1,40 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2025 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ +#ifndef TBOX_UTIL_UUID_H_20250904 +#define TBOX_UTIL_UUID_H_20250904 + +#include + +namespace tbox { +namespace util { + +//! UUID生成策略 +enum class UUIDGenStratey { + kLinuxKernal, //!< 通过读取/proc/sys/kernel/random/uuid生成 + kRandom, //!< 通用随机数生成 +}; + +//! 根据策略生成UUID +std::string GenUUID(UUIDGenStratey stratey = UUIDGenStratey::kLinuxKernal); + +} +} + +#endif //TBOX_UTIL_UUID_H_20250904 -- Gitee From a7cd1c83426e7840d9f42db53da843e4b92f825a Mon Sep 17 00:00:00 2001 From: Hevake Lee Date: Thu, 4 Sep 2025 12:22:50 +0800 Subject: [PATCH 2/2] =?UTF-8?q?feat(jsonrpc):1.12.24,=20=E4=BF=AE=E6=94=B9?= =?UTF-8?q?jsonrpc?= MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit 1.修改example,分别实现string与int的id的示例;修复单元测试不通过的问题; 2.修复单元测试不通过的问题; 3.优化Proto的代码; --- examples/jsonrpc/message/int_id/Makefile | 26 +++ .../message/{ => int_id}/ping/Makefile | 2 +- .../message/{ => int_id}/ping/ping.cpp | 4 +- .../message/{ => int_id}/pong/Makefile | 2 +- .../message/{ => int_id}/pong/pong.cpp | 6 +- examples/jsonrpc/message/str_id/Makefile | 26 +++ .../{req_rsp => message/str_id}/ping/Makefile | 2 +- examples/jsonrpc/message/str_id/ping/ping.cpp | 134 ++++++++++++++ .../{req_rsp => message/str_id}/pong/Makefile | 2 +- examples/jsonrpc/message/str_id/pong/pong.cpp | 128 +++++++++++++ examples/jsonrpc/req_rsp/int_id/Makefile | 26 +++ examples/jsonrpc/req_rsp/int_id/ping/Makefile | 36 ++++ .../req_rsp/{ => int_id}/ping/ping.cpp | 4 +- examples/jsonrpc/req_rsp/int_id/pong/Makefile | 36 ++++ .../req_rsp/{ => int_id}/pong/pong.cpp | 6 +- examples/jsonrpc/req_rsp/str_id/Makefile | 26 +++ examples/jsonrpc/req_rsp/str_id/ping/Makefile | 36 ++++ examples/jsonrpc/req_rsp/str_id/ping/ping.cpp | 135 ++++++++++++++ examples/jsonrpc/req_rsp/str_id/pong/Makefile | 36 ++++ examples/jsonrpc/req_rsp/str_id/pong/pong.cpp | 128 +++++++++++++ modules/jsonrpc/int_id_rpc.h | 1 + modules/jsonrpc/proto.cpp | 170 ++++++++++-------- modules/jsonrpc/proto.h | 5 +- .../protos/header_stream_proto_test.cpp | 2 +- modules/jsonrpc/protos/packet_proto_test.cpp | 2 +- .../jsonrpc/protos/raw_stream_proto_test.cpp | 2 +- modules/jsonrpc/str_id_rpc.h | 1 + modules/util/uuid.cpp | 37 +++- modules/util/uuid.h | 3 +- version.mk | 2 +- 30 files changed, 923 insertions(+), 103 deletions(-) create mode 100644 examples/jsonrpc/message/int_id/Makefile rename examples/jsonrpc/message/{ => int_id}/ping/Makefile (94%) rename examples/jsonrpc/message/{ => int_id}/ping/ping.cpp (97%) rename examples/jsonrpc/message/{ => int_id}/pong/Makefile (94%) rename examples/jsonrpc/message/{ => int_id}/pong/pong.cpp (96%) create mode 100644 examples/jsonrpc/message/str_id/Makefile rename examples/jsonrpc/{req_rsp => message/str_id}/ping/Makefile (94%) create mode 100644 examples/jsonrpc/message/str_id/ping/ping.cpp rename examples/jsonrpc/{req_rsp => message/str_id}/pong/Makefile (94%) create mode 100644 examples/jsonrpc/message/str_id/pong/pong.cpp create mode 100644 examples/jsonrpc/req_rsp/int_id/Makefile create mode 100644 examples/jsonrpc/req_rsp/int_id/ping/Makefile rename examples/jsonrpc/req_rsp/{ => int_id}/ping/ping.cpp (97%) create mode 100644 examples/jsonrpc/req_rsp/int_id/pong/Makefile rename examples/jsonrpc/req_rsp/{ => int_id}/pong/pong.cpp (96%) create mode 100644 examples/jsonrpc/req_rsp/str_id/Makefile create mode 100644 examples/jsonrpc/req_rsp/str_id/ping/Makefile create mode 100644 examples/jsonrpc/req_rsp/str_id/ping/ping.cpp create mode 100644 examples/jsonrpc/req_rsp/str_id/pong/Makefile create mode 100644 examples/jsonrpc/req_rsp/str_id/pong/pong.cpp diff --git a/examples/jsonrpc/message/int_id/Makefile b/examples/jsonrpc/message/int_id/Makefile new file mode 100644 index 0000000..a0b7a8a --- /dev/null +++ b/examples/jsonrpc/message/int_id/Makefile @@ -0,0 +1,26 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +all test clean distclean: + @for i in $(shell ls) ; do \ + if [ -d $$i ]; then \ + $(MAKE) -C $$i $@ || exit $$? ; \ + fi \ + done diff --git a/examples/jsonrpc/message/ping/Makefile b/examples/jsonrpc/message/int_id/ping/Makefile similarity index 94% rename from examples/jsonrpc/message/ping/Makefile rename to examples/jsonrpc/message/int_id/ping/Makefile index 79f243a..aa58710 100644 --- a/examples/jsonrpc/message/ping/Makefile +++ b/examples/jsonrpc/message/int_id/ping/Makefile @@ -18,7 +18,7 @@ # of the source tree. # -PROJECT := examples/jsonrpc/message/ping +PROJECT := examples/jsonrpc/message/int_id/ping EXE_NAME := ${PROJECT} CPP_SRC_FILES := ping.cpp diff --git a/examples/jsonrpc/message/ping/ping.cpp b/examples/jsonrpc/message/int_id/ping/ping.cpp similarity index 97% rename from examples/jsonrpc/message/ping/ping.cpp rename to examples/jsonrpc/message/int_id/ping/ping.cpp index f85e7d9..2604fd0 100644 --- a/examples/jsonrpc/message/ping/ping.cpp +++ b/examples/jsonrpc/message/int_id/ping/ping.cpp @@ -38,7 +38,7 @@ #include //! 导入TcpClient模块 #include //! 使用JSON操作的辅助函数 GetField() #include //! 导入 jsonrpc::RawStreamProto -#include //! 导入 jsonrpc::Rpc +#include //! 导入 jsonrpc::IntIdRpc using namespace tbox; @@ -61,7 +61,7 @@ int main(int argc, char **argv) network::TcpClient tcp_client(loop); jsonrpc::RawStreamProto proto; - jsonrpc::Rpc rpc(loop); + jsonrpc::IntIdRpc rpc(loop); rpc.initialize(&proto, 3); std::string srv_addr = "/tmp/ping_pong.sock"; diff --git a/examples/jsonrpc/message/pong/Makefile b/examples/jsonrpc/message/int_id/pong/Makefile similarity index 94% rename from examples/jsonrpc/message/pong/Makefile rename to examples/jsonrpc/message/int_id/pong/Makefile index 2010af4..a497b81 100644 --- a/examples/jsonrpc/message/pong/Makefile +++ b/examples/jsonrpc/message/int_id/pong/Makefile @@ -18,7 +18,7 @@ # of the source tree. # -PROJECT := examples/jsonrpc/message/pong +PROJECT := examples/jsonrpc/message/int_id/pong EXE_NAME := ${PROJECT} CPP_SRC_FILES := pong.cpp diff --git a/examples/jsonrpc/message/pong/pong.cpp b/examples/jsonrpc/message/int_id/pong/pong.cpp similarity index 96% rename from examples/jsonrpc/message/pong/pong.cpp rename to examples/jsonrpc/message/int_id/pong/pong.cpp index deef565..09b5170 100644 --- a/examples/jsonrpc/message/pong/pong.cpp +++ b/examples/jsonrpc/message/int_id/pong/pong.cpp @@ -35,7 +35,7 @@ #include //! ctrl+c信号事件 #include //! TcpServer #include //! jsonrpc::RawStreamProto -#include //! jsonrpc::Rpc +#include //! jsonrpc::IntIdRpc using namespace tbox; @@ -58,7 +58,7 @@ int main(int argc, char **argv) network::TcpServer tcp_server(loop); jsonrpc::RawStreamProto proto; - jsonrpc::Rpc rpc(loop); + jsonrpc::IntIdRpc rpc(loop); rpc.initialize(&proto, 3); std::string srv_addr = "/tmp/ping_pong.sock"; @@ -101,7 +101,7 @@ int main(int argc, char **argv) rpc.addService("ping", [&] (int id, const Json &js_params, int &, Json &) { int ping_count = 0; util::json::GetField(js_params, "count", ping_count); - LogDbg("got ping_count: %d", ping_count); + LogDbg("id: %d, got ping_count: %d", id, ping_count); rpc.notify("pong", js_params); return false; //! 表示不回复 }); diff --git a/examples/jsonrpc/message/str_id/Makefile b/examples/jsonrpc/message/str_id/Makefile new file mode 100644 index 0000000..a0b7a8a --- /dev/null +++ b/examples/jsonrpc/message/str_id/Makefile @@ -0,0 +1,26 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +all test clean distclean: + @for i in $(shell ls) ; do \ + if [ -d $$i ]; then \ + $(MAKE) -C $$i $@ || exit $$? ; \ + fi \ + done diff --git a/examples/jsonrpc/req_rsp/ping/Makefile b/examples/jsonrpc/message/str_id/ping/Makefile similarity index 94% rename from examples/jsonrpc/req_rsp/ping/Makefile rename to examples/jsonrpc/message/str_id/ping/Makefile index 2b6f3fa..eb49080 100644 --- a/examples/jsonrpc/req_rsp/ping/Makefile +++ b/examples/jsonrpc/message/str_id/ping/Makefile @@ -18,7 +18,7 @@ # of the source tree. # -PROJECT := examples/jsonrpc/req_rsp/ping +PROJECT := examples/jsonrpc/message/str_id/ping EXE_NAME := ${PROJECT} CPP_SRC_FILES := ping.cpp diff --git a/examples/jsonrpc/message/str_id/ping/ping.cpp b/examples/jsonrpc/message/str_id/ping/ping.cpp new file mode 100644 index 0000000..37f66e3 --- /dev/null +++ b/examples/jsonrpc/message/str_id/ping/ping.cpp @@ -0,0 +1,134 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ + +/** + * 这是JsonRpc模块ping/pong示例中ping的一方 + * + * 它主动去连接/tmp/ping_pong.sock,连接成功后就会发送ping请求,并在请 + * 求中附带一个count参数。该count参数在每次请求之前都会自增1。 + * pong端收到ping请求后,将count作为结果回复该请求。 + * ping端收到回复后,就立即发送下一个ping请求。 + * 如此周而复始,直至与pong端断开,或者接收到了SIGINT停止信号 + */ + +#include //! 打印日志 +#include //! 使能日志输出 +#include //! 使用 SetScopeExitAction() +#include //! 操作JSON对象用 +#include //! 对Buffer进行操作 +#include //! 事件循环 +#include //! ctrl+c信号事件 +#include //! 导入TcpClient模块 +#include //! 使用JSON操作的辅助函数 GetField() +#include //! 导入 jsonrpc::RawStreamProto +#include //! 导入 jsonrpc::StrIdRpc + +using namespace tbox; + +int main(int argc, char **argv) +{ + LogOutput_Enable(); + + LogInfo("enter"); + + auto loop = event::Loop::New(); + auto sig_event = loop->newSignalEvent(); + + //! 设置退出时,要释放loop与sig_event + SetScopeExitAction( + [=] { + delete sig_event; + delete loop; + } + ); + + network::TcpClient tcp_client(loop); + jsonrpc::RawStreamProto proto; + jsonrpc::StrIdRpc rpc(loop); + + rpc.initialize(&proto, 3); + std::string srv_addr = "/tmp/ping_pong.sock"; + + tcp_client.initialize(network::SockAddr::FromString(srv_addr)); + //! 设置接收到数据后的处理 + tcp_client.setReceiveCallback([&] (network::Buffer &buff) { + while (buff.readableSize() > 0) { + //! 将buff中的数据交给proto进行解析 + auto ret = proto.onRecvData(buff.readableBegin(), buff.readableSize()); + if (ret > 0) { //! 正常解析 + buff.hasRead(ret); + } else if (ret < 0) { //! 有错误 + tcp_client.stop(); + tcp_client.start(); + } else + break; + } + }, 0); + + //! 设置proto发送数据的方法 + proto.setSendCallback([&] (const void* data_ptr, size_t data_size) { + tcp_client.send(data_ptr, data_size); //! 将数据交给tcp_client去发送 + }); + + tcp_client.start(); + + int ping_count = 0; + std::function send_ping; + //! 定义ping的动作 + send_ping = [&] { + ++ping_count; + Json js_params = {{"count", ping_count}}; //! 组装请求参数 + + //! 发送ping消息 + rpc.notify("ping", js_params); + LogDbg("send ping: %d", ping_count); + }; + + //! 定义收到pong的动作 + rpc.addService("pong", [&] (const std::string &, const Json &js_params, int &, Json &) { + int pong_count = 0; + util::json::GetField(js_params, "count", pong_count); + send_ping(); + return false; + }); + + //! 设置一旦tcp_client连接上就进行ping动作 + tcp_client.setConnectedCallback(send_ping); + + //! 设置程序安全退出条件 + sig_event->initialize(SIGINT, event::Event::Mode::kPersist); + sig_event->enable(); + + //! 设置程序退出动作 + sig_event->setCallback( + [&] (int) { + tcp_client.stop(); + loop->exitLoop(); + } + ); + + LogInfo("start"); + loop->runLoop(); + LogInfo("stop"); + + rpc.cleanup(); + tcp_client.cleanup(); + return 0; +} diff --git a/examples/jsonrpc/req_rsp/pong/Makefile b/examples/jsonrpc/message/str_id/pong/Makefile similarity index 94% rename from examples/jsonrpc/req_rsp/pong/Makefile rename to examples/jsonrpc/message/str_id/pong/Makefile index c050507..c09e2e9 100644 --- a/examples/jsonrpc/req_rsp/pong/Makefile +++ b/examples/jsonrpc/message/str_id/pong/Makefile @@ -18,7 +18,7 @@ # of the source tree. # -PROJECT := examples/jsonrpc/req_rsp/pong +PROJECT := examples/jsonrpc/message/str_id/pong EXE_NAME := ${PROJECT} CPP_SRC_FILES := pong.cpp diff --git a/examples/jsonrpc/message/str_id/pong/pong.cpp b/examples/jsonrpc/message/str_id/pong/pong.cpp new file mode 100644 index 0000000..5181531 --- /dev/null +++ b/examples/jsonrpc/message/str_id/pong/pong.cpp @@ -0,0 +1,128 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ + +/** + * 这是JsonRpc模块ping/pong示例中pong的一方 + * + * 它绑定Unix Domain Sock: /tmp/ping_pong.sock,等待被连接。 + * 当收到ping请求后,将参数中的count提取出来作为结果直接回复。 + */ + +#include //! 打印日志 +#include //! LogOutput_Enable() +#include //! SetScopeExitAction() +#include //! 操作JSON对象用 +#include //! 对Buffer进行操作 +#include //! util::json::GetField() +#include //! 事件循环 +#include //! ctrl+c信号事件 +#include //! TcpServer +#include //! jsonrpc::RawStreamProto +#include //! jsonrpc::StrIdRpc + +using namespace tbox; + +int main(int argc, char **argv) +{ + LogOutput_Enable(); + + LogInfo("enter"); + + auto loop = event::Loop::New(); + auto sig_event = loop->newSignalEvent(); + + //! 设置退出时,要释放loop与sig_event + SetScopeExitAction( + [=] { + delete sig_event; + delete loop; + } + ); + + network::TcpServer tcp_server(loop); + jsonrpc::RawStreamProto proto; + jsonrpc::StrIdRpc rpc(loop); + + rpc.initialize(&proto, 3); + std::string srv_addr = "/tmp/ping_pong.sock"; + + network::TcpServer::ConnToken curr_client_token; //! 当前的客户端 + + tcp_server.initialize(network::SockAddr::FromString(srv_addr), 2); + //! 设置接收到连接后的动作:保存curr_client_token + tcp_server.setConnectedCallback([&] (network::TcpServer::ConnToken client_token) { + tcp_server.disconnect(curr_client_token); + curr_client_token = client_token; + }); + //! 设置连接断开后的动作:清除curr_client_token + tcp_server.setDisconnectedCallback([&] (network::TcpServer::ConnToken client_token) { + curr_client_token.reset(); + }); + //! 设置接收到数据后的处理 + tcp_server.setReceiveCallback([&] (network::TcpServer::ConnToken client_token, network::Buffer &buff) { + while (buff.readableSize() > 0) { + //! 将buff中的数据交给proto进行解析 + auto ret = proto.onRecvData(buff.readableBegin(), buff.readableSize()); + if (ret > 0) { + buff.hasRead(ret); + } else if (ret < 0) { //! 有错误 + tcp_server.disconnect(curr_client_token); + curr_client_token.reset(); + } else + break; + } + }, 0); + + //! 设置proto发送数据的方法 + proto.setSendCallback([&] (const void* data_ptr, size_t data_size) { + tcp_server.send(curr_client_token, data_ptr, data_size); + }); + + tcp_server.start(); //! 启动tcp服务 + + //! 注册ping的服务处理函数 + rpc.addService("ping", [&] (const std::string &id, const Json &js_params, int &, Json &) { + int ping_count = 0; + util::json::GetField(js_params, "count", ping_count); + LogDbg("id: %s, got ping_count: %d", id.c_str(), ping_count); + rpc.notify("pong", js_params); + return false; //! 表示不回复 + }); + + //! 设置程序安全退出条件 + sig_event->initialize(SIGINT, event::Event::Mode::kPersist); + sig_event->enable(); + + //! 设置程序退出动作 + sig_event->setCallback( + [&] (int) { + tcp_server.stop(); + loop->exitLoop(); + } + ); + + LogInfo("start"); + loop->runLoop(); + LogInfo("stop"); + + rpc.cleanup(); + tcp_server.cleanup(); + return 0; +} diff --git a/examples/jsonrpc/req_rsp/int_id/Makefile b/examples/jsonrpc/req_rsp/int_id/Makefile new file mode 100644 index 0000000..a0b7a8a --- /dev/null +++ b/examples/jsonrpc/req_rsp/int_id/Makefile @@ -0,0 +1,26 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +all test clean distclean: + @for i in $(shell ls) ; do \ + if [ -d $$i ]; then \ + $(MAKE) -C $$i $@ || exit $$? ; \ + fi \ + done diff --git a/examples/jsonrpc/req_rsp/int_id/ping/Makefile b/examples/jsonrpc/req_rsp/int_id/ping/Makefile new file mode 100644 index 0000000..ee7367f --- /dev/null +++ b/examples/jsonrpc/req_rsp/int_id/ping/Makefile @@ -0,0 +1,36 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +PROJECT := examples/jsonrpc/req_rsp/int_id/ping +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := ping.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_jsonrpc \ + -ltbox_network \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lpthread -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/jsonrpc/req_rsp/ping/ping.cpp b/examples/jsonrpc/req_rsp/int_id/ping/ping.cpp similarity index 97% rename from examples/jsonrpc/req_rsp/ping/ping.cpp rename to examples/jsonrpc/req_rsp/int_id/ping/ping.cpp index c5578f9..1ae0742 100644 --- a/examples/jsonrpc/req_rsp/ping/ping.cpp +++ b/examples/jsonrpc/req_rsp/int_id/ping/ping.cpp @@ -38,7 +38,7 @@ #include //! ctrl+c信号事件 #include //! 导入TcpClient模块 #include //! 导入 jsonrpc::RawStreamProto -#include //! 导入 jsonrpc::Rpc +#include //! 导入 jsonrpc::IntIdRpc using namespace tbox; @@ -61,7 +61,7 @@ int main(int argc, char **argv) network::TcpClient tcp_client(loop); jsonrpc::RawStreamProto proto; - jsonrpc::Rpc rpc(loop); + jsonrpc::IntIdRpc rpc(loop); rpc.initialize(&proto, 3); std::string srv_addr = "/tmp/ping_pong.sock"; diff --git a/examples/jsonrpc/req_rsp/int_id/pong/Makefile b/examples/jsonrpc/req_rsp/int_id/pong/Makefile new file mode 100644 index 0000000..27b18a2 --- /dev/null +++ b/examples/jsonrpc/req_rsp/int_id/pong/Makefile @@ -0,0 +1,36 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +PROJECT := examples/jsonrpc/req_rsp/int_id/pong +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := pong.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_jsonrpc \ + -ltbox_network \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lpthread -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/jsonrpc/req_rsp/pong/pong.cpp b/examples/jsonrpc/req_rsp/int_id/pong/pong.cpp similarity index 96% rename from examples/jsonrpc/req_rsp/pong/pong.cpp rename to examples/jsonrpc/req_rsp/int_id/pong/pong.cpp index 18cf4f9..8dc881c 100644 --- a/examples/jsonrpc/req_rsp/pong/pong.cpp +++ b/examples/jsonrpc/req_rsp/int_id/pong/pong.cpp @@ -35,7 +35,7 @@ #include //! ctrl+c信号事件 #include //! TcpServer #include //! jsonrpc::RawStreamProto -#include //! jsonrpc::Rpc +#include //! jsonrpc::IntIdRpc using namespace tbox; @@ -58,7 +58,7 @@ int main(int argc, char **argv) network::TcpServer tcp_server(loop); jsonrpc::RawStreamProto proto; - jsonrpc::Rpc rpc(loop); + jsonrpc::IntIdRpc rpc(loop); rpc.initialize(&proto, 3); std::string srv_addr = "/tmp/ping_pong.sock"; @@ -101,7 +101,7 @@ int main(int argc, char **argv) rpc.addService("ping", [&] (int id, const Json &js_params, int &errcode, Json &js_result) { int ping_count = 0; util::json::GetField(js_params, "count", ping_count); - LogDbg("got ping_count: %d", ping_count); + LogDbg("id:%d, got ping_count: %d", id, ping_count); js_result = js_params; return true; //! 表示在函数返回后立即发送回复 }); diff --git a/examples/jsonrpc/req_rsp/str_id/Makefile b/examples/jsonrpc/req_rsp/str_id/Makefile new file mode 100644 index 0000000..a0b7a8a --- /dev/null +++ b/examples/jsonrpc/req_rsp/str_id/Makefile @@ -0,0 +1,26 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +all test clean distclean: + @for i in $(shell ls) ; do \ + if [ -d $$i ]; then \ + $(MAKE) -C $$i $@ || exit $$? ; \ + fi \ + done diff --git a/examples/jsonrpc/req_rsp/str_id/ping/Makefile b/examples/jsonrpc/req_rsp/str_id/ping/Makefile new file mode 100644 index 0000000..d62a8e5 --- /dev/null +++ b/examples/jsonrpc/req_rsp/str_id/ping/Makefile @@ -0,0 +1,36 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +PROJECT := examples/jsonrpc/req_rsp/str_id/ping +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := ping.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_jsonrpc \ + -ltbox_network \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lpthread -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/jsonrpc/req_rsp/str_id/ping/ping.cpp b/examples/jsonrpc/req_rsp/str_id/ping/ping.cpp new file mode 100644 index 0000000..f1d3b28 --- /dev/null +++ b/examples/jsonrpc/req_rsp/str_id/ping/ping.cpp @@ -0,0 +1,135 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ + +/** + * 这是JsonRpc模块ping/pong示例中ping的一方 + * + * 它主动去连接/tmp/ping_pong.sock,连接成功后就会发送ping请求,并在请 + * 求中附带一个count参数。该count参数在每次请求之前都会自增1。 + * pong端收到ping请求后,将count作为结果回复该请求。 + * ping端收到回复后,就立即发送下一个ping请求。 + * 如此周而复始,直至与pong端断开,或者接收到了SIGINT停止信号 + */ + +#include //! 打印日志 +#include //! 使能日志输出 +#include //! 使用 SetScopeExitAction() +#include //! 操作JSON对象用 +#include //! 对Buffer进行操作 +#include //! 使用JSON操作的辅助函数 GetField() +#include //! 事件循环 +#include //! ctrl+c信号事件 +#include //! 导入TcpClient模块 +#include //! 导入 jsonrpc::RawStreamProto +#include //! 导入 jsonrpc::StrIdRpc + +using namespace tbox; + +int main(int argc, char **argv) +{ + LogOutput_Enable(); + + LogInfo("enter"); + + auto loop = event::Loop::New(); + auto sig_event = loop->newSignalEvent(); + + //! 设置退出时,要释放loop与sig_event + SetScopeExitAction( + [=] { + delete sig_event; + delete loop; + } + ); + + network::TcpClient tcp_client(loop); + jsonrpc::RawStreamProto proto; + jsonrpc::StrIdRpc rpc(loop); + + rpc.initialize(&proto, 3); + std::string srv_addr = "/tmp/ping_pong.sock"; + + tcp_client.initialize(network::SockAddr::FromString(srv_addr)); + //! 设置接收到数据后的处理 + tcp_client.setReceiveCallback([&] (network::Buffer &buff) { + while (buff.readableSize() > 0) { + //! 将buff中的数据交给proto进行解析 + auto ret = proto.onRecvData(buff.readableBegin(), buff.readableSize()); + if (ret > 0) { //! 正常解析 + buff.hasRead(ret); + } else if (ret < 0) { //! 有错误 + tcp_client.stop(); + tcp_client.start(); + } else + break; + } + }, 0); + + //! 设置proto发送数据的方法 + proto.setSendCallback([&] (const void* data_ptr, size_t data_size) { + tcp_client.send(data_ptr, data_size); //! 将数据交给tcp_client去发送 + }); + + tcp_client.start(); + + int ping_count = 0; + std::function send_ping; + //! 定义ping动作 + send_ping = [&] { + ++ping_count; + Json js_params = {{"count", ping_count}}; //! 组装请求参数 + + //! 发送ping请求,并在收到回复后,进行下一个ping动作 + rpc.request("ping", js_params, + [&] (int errcode, const Json &js_result) { + int pong_count = 0; + util::json::GetField(js_result, "count", pong_count); + if (errcode == 0) { + LogDbg("got pong: %d", pong_count); + send_ping(); + } else + LogNotice("got erro: %d", errcode); + }); + LogDbg("send ping: %d", ping_count); + }; + + //! 设置一旦tcp_client连接上就进行ping动作 + tcp_client.setConnectedCallback(send_ping); + + //! 设置程序安全退出条件 + sig_event->initialize(SIGINT, event::Event::Mode::kPersist); + sig_event->enable(); + + //! 设置程序退出动作 + sig_event->setCallback( + [&] (int) { + tcp_client.stop(); + loop->exitLoop(); + } + ); + + LogInfo("start"); + loop->runLoop(); + LogInfo("stop"); + + rpc.cleanup(); + tcp_client.cleanup(); + return 0; +} diff --git a/examples/jsonrpc/req_rsp/str_id/pong/Makefile b/examples/jsonrpc/req_rsp/str_id/pong/Makefile new file mode 100644 index 0000000..f6f0206 --- /dev/null +++ b/examples/jsonrpc/req_rsp/str_id/pong/Makefile @@ -0,0 +1,36 @@ +# +# .============. +# // M A K E / \ +# // C++ DEV / \ +# // E A S Y / \/ \ +# ++ ----------. \/\ . +# \\ \ \ /\ / +# \\ \ \ / +# \\ \ \ / +# -============' +# +# Copyright (c) 2018 Hevake and contributors, all rights reserved. +# +# This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) +# Use of this source code is governed by MIT license that can be found +# in the LICENSE file in the root of the source tree. All contributing +# project authors may be found in the CONTRIBUTORS.md file in the root +# of the source tree. +# + +PROJECT := examples/jsonrpc/req_rsp/str_id/pong +EXE_NAME := ${PROJECT} + +CPP_SRC_FILES := pong.cpp + +CXXFLAGS := -DMODULE_ID='"$(EXE_NAME)"' $(CXXFLAGS) +LDFLAGS += \ + -ltbox_jsonrpc \ + -ltbox_network \ + -ltbox_eventx \ + -ltbox_event \ + -ltbox_util \ + -ltbox_base \ + -lpthread -ldl + +include $(TOP_DIR)/mk/exe_common.mk diff --git a/examples/jsonrpc/req_rsp/str_id/pong/pong.cpp b/examples/jsonrpc/req_rsp/str_id/pong/pong.cpp new file mode 100644 index 0000000..91607a7 --- /dev/null +++ b/examples/jsonrpc/req_rsp/str_id/pong/pong.cpp @@ -0,0 +1,128 @@ +/* + * .============. + * // M A K E / \ + * // C++ DEV / \ + * // E A S Y / \/ \ + * ++ ----------. \/\ . + * \\ \ \ /\ / + * \\ \ \ / + * \\ \ \ / + * -============' + * + * Copyright (c) 2023 Hevake and contributors, all rights reserved. + * + * This file is part of cpp-tbox (https://github.com/cpp-main/cpp-tbox) + * Use of this source code is governed by MIT license that can be found + * in the LICENSE file in the root of the source tree. All contributing + * project authors may be found in the CONTRIBUTORS.md file in the root + * of the source tree. + */ + +/** + * 这是JsonRpc模块ping/pong示例中pong的一方 + * + * 它绑定Unix Domain Sock: /tmp/ping_pong.sock,等待被连接。 + * 当收到ping请求后,将参数中的count提取出来作为结果直接回复。 + */ + +#include //! 打印日志 +#include //! LogOutput_Enable() +#include //! SetScopeExitAction() +#include //! 操作JSON对象用 +#include //! 对Buffer的操作 +#include //! util::json::GetField() +#include //! 事件循环 +#include //! ctrl+c信号事件 +#include //! TcpServer +#include //! jsonrpc::RawStreamProto +#include //! jsonrpc::StrIdRpc + +using namespace tbox; + +int main(int argc, char **argv) +{ + LogOutput_Enable(); + + LogInfo("enter"); + + auto loop = event::Loop::New(); + auto sig_event = loop->newSignalEvent(); + + //! 设置退出时,要释放loop与sig_event + SetScopeExitAction( + [=] { + delete sig_event; + delete loop; + } + ); + + network::TcpServer tcp_server(loop); + jsonrpc::RawStreamProto proto; + jsonrpc::StrIdRpc rpc(loop); + + rpc.initialize(&proto, 3); + std::string srv_addr = "/tmp/ping_pong.sock"; + + network::TcpServer::ConnToken curr_client_token; //! 当前的客户端 + + tcp_server.initialize(network::SockAddr::FromString(srv_addr), 2); + //! 设置接收到连接后的动作:保存curr_client_token + tcp_server.setConnectedCallback([&] (network::TcpServer::ConnToken client_token) { + tcp_server.disconnect(curr_client_token); + curr_client_token = client_token; + }); + //! 设置连接断开后的动作:清除curr_client_token + tcp_server.setDisconnectedCallback([&] (network::TcpServer::ConnToken client_token) { + curr_client_token.reset(); + }); + //! 设置接收到数据后的处理 + tcp_server.setReceiveCallback([&] (network::TcpServer::ConnToken client_token, network::Buffer &buff) { + while (buff.readableSize() > 0) { + //! 将buff中的数据交给proto进行解析 + auto ret = proto.onRecvData(buff.readableBegin(), buff.readableSize()); + if (ret > 0) { + buff.hasRead(ret); + } else if (ret < 0) { //! 有错误 + tcp_server.disconnect(curr_client_token); + curr_client_token.reset(); + } else + break; + } + }, 0); + + //! 设置proto发送数据的方法 + proto.setSendCallback([&] (const void* data_ptr, size_t data_size) { + tcp_server.send(curr_client_token, data_ptr, data_size); + }); + + tcp_server.start(); //! 启动tcp服务 + + //! 注册ping的服务处理函数 + rpc.addService("ping", [&] (const std::string &id, const Json &js_params, int &errcode, Json &js_result) { + int ping_count = 0; + util::json::GetField(js_params, "count", ping_count); + LogDbg("id:%s, got ping_count: %d", id.c_str(), ping_count); + js_result = js_params; + return true; //! 表示在函数返回后立即发送回复 + }); + + //! 设置程序安全退出条件 + sig_event->initialize(SIGINT, event::Event::Mode::kPersist); + sig_event->enable(); + + //! 设置程序退出动作 + sig_event->setCallback( + [&] (int) { + tcp_server.stop(); + loop->exitLoop(); + } + ); + + LogInfo("start"); + loop->runLoop(); + LogInfo("stop"); + + rpc.cleanup(); + tcp_server.cleanup(); + return 0; +} diff --git a/modules/jsonrpc/int_id_rpc.h b/modules/jsonrpc/int_id_rpc.h index c69e5b1..e6603db 100644 --- a/modules/jsonrpc/int_id_rpc.h +++ b/modules/jsonrpc/int_id_rpc.h @@ -34,6 +34,7 @@ class Proto; class IntIdRpc { public: + //! ID类型为int using IdType = int; using IdTypeConstRef = int; diff --git a/modules/jsonrpc/proto.cpp b/modules/jsonrpc/proto.cpp index 86ba642..0fcc5f6 100644 --- a/modules/jsonrpc/proto.cpp +++ b/modules/jsonrpc/proto.cpp @@ -164,7 +164,7 @@ void Proto::cleanup() send_data_cb_ = nullptr; } -void Proto::onRecvJson(const Json &js) +void Proto::onRecvJson(const Json &js) const { if (js.is_object()) { @@ -175,82 +175,11 @@ void Proto::onRecvJson(const Json &js) } if (js.contains("method")) { - //! 按请求进行处理 - std::string method; - if (!util::json::GetField(js, "method", method)) { - LogNotice("method type not string"); - return; - } - - auto &js_params = js.contains("params") ? js["params"] : Json(); - - if (id_type_ == IdType::kInt) { - int id = 0; - util::json::GetField(js, "id", id); - recv_request_int_cb_(id, method, js_params); - - } else if (id_type_ == IdType::kString) { - std::string id; - util::json::GetField(js, "id", id); - recv_request_str_cb_(id, method, js_params); - - } else { - LogWarn("please invoke setRecvCallback() first."); - } - + handleAsMethod(js); //! 按请求进行处理 } else if (js.contains("result")) { - //! 按结果回复进行处理 - auto &js_result = js["result"]; - - if (id_type_ == IdType::kInt) { - int id = 0; - if (!util::json::GetField(js, "id", id)) { - LogNotice("no int id field in respond"); - return; - } - recv_respond_int_cb_(id, 0, js_result); - - } else if (id_type_ == IdType::kString) { - std::string id; - if (!util::json::GetField(js, "id", id)) { - LogNotice("no string id field in respond"); - return; - } - recv_respond_str_cb_(id, 0, js_result); - - } else { - LogWarn("please invoke setRecvCallback() first."); - } - + handleAsResult(js); //! 按结果回复进行处理,必须要有id字段 } else if (js.contains("error")) { - //! 按错误回复进行处理 - auto &js_error = js["error"]; - int errcode = 0; - if (!util::json::GetField(js_error, "code", errcode)) { - LogNotice("no code field in error"); - return; - } - - if (id_type_ == IdType::kInt) { - int id = 0; - if (!util::json::GetField(js, "id", id)) { - LogNotice("no int id field in respond"); - return; - } - recv_respond_int_cb_(id, errcode, Json()); - - } else if (id_type_ == IdType::kString) { - std::string id; - if (!util::json::GetField(js, "id", id)) { - LogNotice("no string id field in respond"); - return; - } - recv_respond_str_cb_(id, errcode, Json()); - - } else { - LogWarn("please invoke setRecvCallback() first."); - } - + handleAsError(js); //! 按错误回复进行处理 } else { LogNotice("not jsonrpc format"); } @@ -262,5 +191,96 @@ void Proto::onRecvJson(const Json &js) } } +void Proto::handleAsMethod(const Json &js) const +{ + std::string method; + if (!util::json::GetField(js, "method", method)) { + LogNotice("method type not string"); + return; + } + + auto &js_params = js.contains("params") ? js["params"] : Json(); + + if (id_type_ == IdType::kInt) { + int id = 0; + if (js.contains("id") && !util::json::GetField(js, "id", id)) { + LogNotice("id type not int"); + return; + } + recv_request_int_cb_(id, method, js_params); + + } else if (id_type_ == IdType::kString) { + std::string id; + if (js.contains("id") && !util::json::GetField(js, "id", id)) { + LogNotice("id type not string"); + return; + } + recv_request_str_cb_(id, method, js_params); + + } else { + LogWarn("please invoke setRecvCallback() first."); + } +} + +void Proto::handleAsResult(const Json &js) const +{ + if (!js.contains("id")) { + LogNotice("no id in respond"); + return; + } + + auto &js_result = js["result"]; + + if (id_type_ == IdType::kInt) { + int id = 0; + if (!util::json::GetField(js, "id", id)) { + LogNotice("id type not int in respond"); + return; + } + recv_respond_int_cb_(id, 0, js_result); + + } else if (id_type_ == IdType::kString) { + std::string id; + if (!util::json::GetField(js, "id", id)) { + LogNotice("id type not string in respond"); + return; + } + recv_respond_str_cb_(id, 0, js_result); + + } else { + LogWarn("please invoke setRecvCallback() first."); + } +} + +void Proto::handleAsError(const Json &js) const +{ + auto &js_error = js["error"]; + int errcode = 0; + if (!util::json::GetField(js_error, "code", errcode)) { + LogNotice("no code field in error"); + return; + } + + if (id_type_ == IdType::kInt) { + int id = 0; + if (js.contains("id") && !util::json::GetField(js, "id", id)) { + LogNotice("id type not int in error"); + return; + } + recv_respond_int_cb_(id, errcode, Json()); + + } else if (id_type_ == IdType::kString) { + std::string id; + if (js.contains("id") && !util::json::GetField(js, "id", id)) { + LogNotice("id type not string in error"); + return; + } + recv_respond_str_cb_(id, errcode, Json()); + + } else { + LogWarn("please invoke setRecvCallback() first."); + } +} + } } diff --git a/modules/jsonrpc/proto.h b/modules/jsonrpc/proto.h index 15fefd2..e6a826d 100644 --- a/modules/jsonrpc/proto.h +++ b/modules/jsonrpc/proto.h @@ -64,7 +64,10 @@ class Proto { virtual void sendJson(const Json &js) = 0; - void onRecvJson(const Json &js); + void onRecvJson(const Json &js) const; + void handleAsMethod(const Json &js) const; + void handleAsResult(const Json &js) const; + void handleAsError(const Json &js) const; IdType id_type_ = IdType::kNone; RecvRequestIntCallback recv_request_int_cb_; diff --git a/modules/jsonrpc/protos/header_stream_proto_test.cpp b/modules/jsonrpc/protos/header_stream_proto_test.cpp index a0dee61..29a53dc 100644 --- a/modules/jsonrpc/protos/header_stream_proto_test.cpp +++ b/modules/jsonrpc/protos/header_stream_proto_test.cpp @@ -160,7 +160,7 @@ TEST(HeaderStreamProto, RecvUncompleteData) { EXPECT_EQ(js_params, Json()); ++count; }, - nullptr + [] (int id, int errcode, const Json &result) { } ); const char *str_1 = "\xEA\x53\x00\x00\x00\x28{\"id\":1,\"meth"; diff --git a/modules/jsonrpc/protos/packet_proto_test.cpp b/modules/jsonrpc/protos/packet_proto_test.cpp index 9e940aa..21d38c8 100644 --- a/modules/jsonrpc/protos/packet_proto_test.cpp +++ b/modules/jsonrpc/protos/packet_proto_test.cpp @@ -160,7 +160,7 @@ TEST(PacketProto, RecvUncompleteData) { EXPECT_EQ(js_params, Json()); ++count; }, - nullptr + [] (int id, int errcode, const Json &result) { } ); const char *str_1 = R"({"id":1,"meth)"; diff --git a/modules/jsonrpc/protos/raw_stream_proto_test.cpp b/modules/jsonrpc/protos/raw_stream_proto_test.cpp index e05b665..9387a22 100644 --- a/modules/jsonrpc/protos/raw_stream_proto_test.cpp +++ b/modules/jsonrpc/protos/raw_stream_proto_test.cpp @@ -160,7 +160,7 @@ TEST(RawStreamProto, RecvUncompleteData) { EXPECT_EQ(js_params, Json()); ++count; }, - nullptr + [] (int id, int errcode, const Json &result) { } ); const char *str_1 = R"({"id":1,"meth)"; diff --git a/modules/jsonrpc/str_id_rpc.h b/modules/jsonrpc/str_id_rpc.h index d6bf22d..9a17bfa 100644 --- a/modules/jsonrpc/str_id_rpc.h +++ b/modules/jsonrpc/str_id_rpc.h @@ -34,6 +34,7 @@ class Proto; class StrIdRpc { public: + //! ID类型为std::string using IdType = std::string; using IdTypeConstRef = const std::string&; diff --git a/modules/util/uuid.cpp b/modules/util/uuid.cpp index e1fc879..8884784 100644 --- a/modules/util/uuid.cpp +++ b/modules/util/uuid.cpp @@ -18,25 +18,48 @@ * of the source tree. */ #include "uuid.h" -#include "fs.h" + +#include +#include namespace tbox { namespace util { namespace { -std::string _GenByLinuxKernal() +std::string _GenByRandom() { - std::string uuid; - fs::ReadFirstLineFromTextFile("/proc/sys/kernel/random/uuid", uuid); - return uuid; + std::random_device rd; + std::mt19937 gen(rd()); + std::uniform_int_distribution<> dis(0, 15); + std::uniform_int_distribution<> dis2(8, 11); + + std::ostringstream ss; + ss << std::hex; + for (int i = 0; i < 8; ++i) + ss << dis(gen); + ss << '-'; + for (int i = 0; i < 4; ++i) + ss << dis(gen); + ss << "-4"; + for (int i = 0; i < 3; ++i) + ss << dis(gen); + ss << '-'; + ss << dis2(gen); + for (int i = 0; i < 3; ++i) + ss << dis(gen); + ss << '-'; + for (int i = 0; i < 12; ++i) + ss << dis(gen); + + return ss.str(); } } std::string GenUUID(UUIDGenStratey stratey) { switch (stratey) { - case UUIDGenStratey::kLinuxKernal: - return _GenByLinuxKernal(); + case UUIDGenStratey::kRandom: + return _GenByRandom(); default: return ""; } diff --git a/modules/util/uuid.h b/modules/util/uuid.h index 851b588..f04fb42 100644 --- a/modules/util/uuid.h +++ b/modules/util/uuid.h @@ -27,12 +27,11 @@ namespace util { //! UUID生成策略 enum class UUIDGenStratey { - kLinuxKernal, //!< 通过读取/proc/sys/kernel/random/uuid生成 kRandom, //!< 通用随机数生成 }; //! 根据策略生成UUID -std::string GenUUID(UUIDGenStratey stratey = UUIDGenStratey::kLinuxKernal); +std::string GenUUID(UUIDGenStratey stratey = UUIDGenStratey::kRandom); } } diff --git a/version.mk b/version.mk index e648dfa..b29ab20 100644 --- a/version.mk +++ b/version.mk @@ -21,4 +21,4 @@ # TBOX版本号 TBOX_VERSION_MAJOR := 1 TBOX_VERSION_MINOR := 12 -TBOX_VERSION_REVISION := 23 +TBOX_VERSION_REVISION := 24 -- Gitee