Asio based Asynchronous Rpc
An implementation of the ping / pong service.
protobuf message definition:
syntax="proto3";
packagepb;
messagerequest
{
optionalbytescommand=1;
}
messageresponse
{
optionalbytesresults=1;
}
serviceservice
{
rpcexecute(request) returns (response);
}
optioncc_generic_services=true;client side implementation:
#defineBOOST_ASIO_HAS_IO_URING
#defineBOOST_ASIO_DISABLE_EPOLL
#include<thread>
#include<iostream>
#include<arpc.hpp>
#include<ping.pb.h>namespacenet= boost::asio;
namespacegp= google::protobuf;
structtask
{
arpc::controller controller;
pb::request request;
pb::response response;
arpc::Closure* done;
};
classclient
{
public:client(net::io_context& ioc, const std::string& host, const std::string& port) : ioc(ioc), work(ioc), host(host), port(port)
{
channel = newarpc::channel(ioc);
service = newpb::service::Stub(channel, pb::service::STUB_OWNS_CHANNEL);
}
voidping()
{
auto t = std::make_shared<task>();
auto& controller = t->controller;
controller.host(host);
controller.port(port);
controller.timeout(80);
t->request.set_command("ping");
t->done = gp::NewCallback(this, &client::done, t);
service->execute(&t->controller, &t->request, &t->response, t->done);
}
voiddone(std::shared_ptr<task> t)
{
auto& controller = t->controller;
if (controller.Failed())
std::cerr << "ErrorCode: " << controller.ErrorCode() << " ErrorText: " << controller.ErrorText() << std::endl;
std::cout << t->response.DebugString();
}
~client()
{
delete service;
}
private:
net::io_context& ioc;
net::io_context::work work;
arpc::channel* channel;
pb::service* service;
std::string host;
std::string port;
};
intmain(int argc, char* argv[])
{
if (argc != 3)
{
std::cout << "Usage: " << argv[0] << " <host> <port>" << std::endl;
return1;
}
std::string host(argv[1]);
std::string port(argv[2]);
net::io_context ioc;
client c(ioc, host, port);
std::thread t([&]{ ioc.run(); });
constexprsize_t size = 100;
char buff[size];
while (fgets(buff, size, stdin))
c.ping();
t.join();
return0;
}server side implementation:
#defineBOOST_ASIO_HAS_IO_URING
#defineBOOST_ASIO_DISABLE_EPOLL
#include<iostream>
#include<arpc.hpp>
#include<ping.pb.h>namespacenet= boost::asio;
namespacegp= google::protobuf;
voiddone()
{
std::cout << "got called" << std::endl;
}
classservice : publicpb::service
{
public:service()
{
}
voidexecute(gp::RpcController* controller, const pb::request* request, pb::response* response, gp::Closure* done)
{
std::cout << request->DebugString();
if (request->command() != "ping") controller->SetFailed("unknown command");
else
response->set_results("pong");
done->Run();
}
~service()
{
}
};
intmain(int argc, char* argv[])
{
if (argc != 3)
{
std::cout << "Usage: " << argv[0] << " <host> <port>" << std::endl;
return1;
}
std::string host(argv[1]);
std::string port(argv[2]);
net::io_context ioc;
service s;
arpc::server server(ioc, host, port);
server.register_service(&s, gp::NewPermanentCallback(&done));
server.run();
ioc.run();
return0;
}arpc is a Remote Procedure Call (RPC) library, which is header-only, extensible and modern C++ oriented.
It's built on top off the boost and protobuf, it's based on the Proactor design pattern with performance in mind.
arpc enables you to do network programming with tcp protocol in a straightforward, asynchronous and OOP manner.
arpc provides the following features:
- timeout The upper limit of the total time for the call to time out between a RPC request and response
- callback The callable to be invoked immediately after a RPC request or response has been accepted and processed
- controller A way to manipulate settings specific to the RPC implementation and to find out about RPC-level errors
By default, the example is using the io_uring backend, it's disabled by default in Boost.Asio.
This backend may be used for all I/O objects, including sockets, timers, and posix descriptors.
To disable the io_uring backend, just comment out the following lines in code under the example directory:
#defineBOOST_ASIO_HAS_IO_URING
#defineBOOST_ASIO_DISABLE_EPOLLThe library relies on a C++20 compiler and standard library
More specifically, arpc requires a compiler/standard library supporting the following C++20 features (non-exhaustively):
- concepts
- lambda templates
- All the C++20 type traits from the <type_traits> header
arpc is header-only. To use it just add the necessary #include line to your source files, like this:
#include<arpc.hpp>To build the example with cmake, cd to the root of the project and setup the build directory:
mkdir build
cd build
cmake ..Make and install the executables:
make -j4
make install
The executables are now located at the bin directory of the root of the project.
The example can also be built with the script build.sh, just run it, the executables will be put at the /tmp directory.
Please see example.
arpc is licensed as Boost Software License 1.0.