a tiny C++14 rpc library, supports all platforms (macOS, Linux, Windows, iOS, Android, etc.) and most microchips ( Arduino, STM32, ESP32/ESP8266, etc.)
Recommend TCP-based implementation: asio_net
Full-feature rpc frameworks (e.g. gRPC
and bRPC
) are very complex and not suitable for use on embedded platforms.
This project offers a lightweight and user-friend rpc library that is better suited for one-to-one rpc calls. It supports all platforms and a wide range of microchips, including Arduino, STM32, ESP32/ESP8266, and more.
Note: This library only offers the protocol layer and API, it does not include the implementation of the transport layer. For TCP-based implementation: asio_net
- Header-Only
- No-Schema
- Support performance-limited platforms including microchips
- Support any connection type (
tcp socket
,serial port
, etc.) - High Performance Serialization, support most STL containers and user type
- Serialization plugins implementations for
flatbuffers
andnlohmann::json
- Support
co_await
, depend onC++20
andasio
, or custom implementation - Support subscribe async callback, async coroutine, and custom scheduler
- RAII-based
dispose
for automatic cancel request - Support timeout, retry, cancel api
- Comprehensive unittests
-
C++
-
Rust
- C++14
- Provide your connection implementation: connection
NOTICE: complete data packets are required for data transmission, such aswebsocket
.
If usingtcp socket
,serial port
, etc., message pack and unpack need to be implemented. Or you can use stream_connection. - Optional: C++20 (for coroutine api, co_await async_call)
- async callback:
// The Receiver
rpc->subscribe("cmd", [](const std::string& msg) -> std::string {
assert(msg == "hello");
return "world";
});
// The Sender
rpc->cmd("cmd")
->msg(std::string("hello"))
->rsp([](const std::string& rsp) {
assert(rsp == "world");
})
->call();
- async coroutine:
// The Receiver
rpc->subscribe("cmd", [&](request_response<std::string, std::string> rr) -> asio::awaitable<void> {
assert(rr->req == "hello");
asio::steady_timer timer(context);
timer.expires_after(std::chrono::seconds(1));
co_await timer.async_wait();
rr->rsp("world");
}, scheduler_asio_coroutine);
// The Sender
// use C++20 co_await with asio, or you can use custom async implementation, and co_await it!
auto rsp = co_await rpc->cmd("cmd")->msg(std::string("hello"))->async_call<std::string>();
assert(rsp.data == "world");
Inspect the code for more details: rpc_s_coroutine.cpp and rpc_c_coroutine.cpp
- Addition:
msg
andrsp
support any serializable type, refer to Serialization.- Detailed usages and unittests can be found here: rpc_test.cpp
- There is an example shows custom async impl: rpc_c_coroutine.hpp
High-performance and memory-saving binary serialization.
- api is very simple to use: include/rpc_core/serialize_api.hpp
- usage and comprehensive unittest: test/test_serialize.cpp
- the design balance cpu and memory usage, and zero-copy if possible.
- std::string is used as inner data container, it's serialize/deserialize is zero-overhead. so, it is recommended to use std::string whenever possible, using it to store binary data is also a good choice.
Fist of all, I want to keep rpc_core
library standalone, without any dependencies, except for STL.
Moreover, these serialization libraries do not align with my design goals:
- protobuf library is too large for some platforms, and it's Varint, Zigzag, and GZIP will use a lot of cpu.
- msgpack has similarity reason to protobuf.
- flatbuffers serialized data is too large.
Of course, when communicating across languages, it is recommended to use the above serialization libraries!
Finally, it also provides a way to use thirdparty serialization libraries directly, refer to Serialization Plugins.
For example, user data:
struct Type {
uint8_t id = 1;
uint8_t age = 18;
std::string name = "test";
};
json: {"id":1,"age":18,"name":"test"}
library | bytes |
---|---|
json | 31 |
flatbuffers | 44 |
protobuf | 10 |
msgpack | 8 |
rpc_core | 8 |
- std::string
- std::wstring
- std::array
- std::vector
- std::list
- std::forward_list
- std::deque
- std::pair
- std::tuple
- std::map
- std::unordered_map
- std::multimap
- std::unordered_multimap
- std::set
- std::unordered_set
- std::multiset
- std::unordered_multiset
- std::stack
- std::queue
- std::priority_queue
- std::bitset
- std::complex
- std::chrono::duration
- std::chrono::time_point
- std::unique_ptr
- std::shared_ptr
- rpc_core::binary_wrap
- custom struct/class
choose
#include "rpc_core/serialize.hpp" struct TestStruct { uint8_t a; std::string b; OtherType c // RPC_CORE_DEFINE_TYPE_INNER(a, b, c); }; RPC_CORE_DEFINE_TYPE(TestStruct, a, b, c);
RPC_CORE_DEFINE_TYPE
orRPC_CORE_DEFINE_TYPE_INNER
for private member variable.
-
flatbuffers.hpp
Supports using types generated byflatbuffers
directly as message
(add the option--gen-object-api
when usingflatc
) -
json_msg.hpp
Supports using types supported by nlohmann/json directly as message
(theto_json/from_json
rules innlohmann/json
need to be satisfied, and useDEFINE_JSON_CLASS
). -
json.hpp
A flexible way to usenlohmann/json
This project is licensed under the MIT license.