impove ipc

master
Сергей Маринкевич 2 months ago
parent 1f16ef6b37
commit 99ea8d1646

@ -3,38 +3,60 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
// Примитивный IPCформат сообщений: // Примитивное IPCсообщение с API add<T>() / get<T>().
// комбинация std::ostringstream / std::istringstream с удобным API add<T>/read<T>. // Под капотом пока текстовый формат, но снаружи интерфейс не завязан на std::string.
class IpcMessageWriter { class IpcMessage {
public: public:
IpcMessage() = default;
explicit IpcMessage(const std::string& raw)
: raw_(raw) {}
IpcMessage(const IpcMessage& other)
: raw_(other.raw_) {}
IpcMessage& operator=(const IpcMessage& other) {
if (this != &other) {
raw_ = other.raw_;
in_.clear();
in_initialized_ = false;
out_.str(std::string{});
}
return *this;
}
// Конструирование исходящего сообщения.
template<typename T> template<typename T>
void add(const T& v) { void add(const T& v) {
out_ << v << ' '; out_ << v << ' ';
raw_ = out_.str();
} }
std::string str() const { // Чтение входящего сообщения по частям.
return out_.str();
}
private:
std::ostringstream out_;
};
class IpcMessageReader {
public:
explicit IpcMessageReader(const std::string& data)
: in_(data) {}
template<typename T> template<typename T>
T read() { T get() {
if (!in_initialized_) {
in_.str(raw_);
in_initialized_ = true;
}
T v{}; T v{};
in_ >> v; in_ >> v;
return v; return v;
} }
const std::string& raw() const {
return raw_;
}
bool empty() const {
return raw_.empty();
}
private: private:
std::string raw_;
std::ostringstream out_;
std::istringstream in_; std::istringstream in_;
bool in_initialized_{false};
}; };

@ -6,10 +6,8 @@
#include <sys/stat.h> #include <sys/stat.h>
#include <unistd.h> #include <unistd.h>
#include <string>
// IPCканал поверх именованных pipe. // IPCканал поверх именованных pipe.
// Инкапсулирует работу с файловыми дескрипторами и обмен строковыми сообщениями. // Инкапсулирует работу с файловыми дескрипторами и обмен сообщениями IpcMessage.
class IpcPipeChannel : public RpcChannel { class IpcPipeChannel : public RpcChannel {
public: public:
@ -28,25 +26,26 @@ public:
} }
} }
void send(const std::string& data) override { void send(const IpcMessage& msg) override {
if (fdOut_ < 0) { if (fdOut_ < 0) {
return; return;
} }
const std::string& data = msg.raw();
::write(fdOut_, data.c_str(), data.size()); ::write(fdOut_, data.c_str(), data.size());
::write(fdOut_, "\n", 1); ::write(fdOut_, "\n", 1);
} }
std::string receive() override { IpcMessage receive() override {
if (fdIn_ < 0) { if (fdIn_ < 0) {
return {}; return IpcMessage{};
} }
char buf[4096]; char buf[4096];
const int n = ::read(fdIn_, buf, sizeof(buf) - 1); const int n = ::read(fdIn_, buf, sizeof(buf) - 1);
if (n <= 0) { if (n <= 0) {
return {}; return IpcMessage{};
} }
buf[n] = 0; buf[n] = 0;
return std::string(buf); return IpcMessage(std::string(buf));
} }
private: private:
@ -54,4 +53,3 @@ private:
int fdOut_{-1}; int fdOut_{-1};
}; };

@ -1,11 +1,10 @@
#pragma once #pragma once
#include <string> #include "IpcMessage.h"
#include <vector>
class RpcChannel { class RpcChannel {
public: public:
virtual ~RpcChannel() = default; virtual ~RpcChannel() = default;
virtual void send(const std::string& data) = 0; virtual void send(const IpcMessage& msg) = 0;
virtual std::string receive() = 0; virtual IpcMessage receive() = 0;
}; };

@ -1,9 +1,6 @@
#pragma once #pragma once
#include "RpcChannel.h" #include "RpcChannel.h"
#include "IpcMessage.h"
#include <string>
class RpcClient { class RpcClient {
public: public:
@ -11,7 +8,7 @@ public:
template<typename Ret, typename... Args> template<typename Ret, typename... Args>
Ret call(const std::string& method, const Args&... args) { Ret call(const std::string& method, const Args&... args) {
IpcMessageWriter msg; IpcMessage msg;
// имя метода // имя метода
msg.add(method); msg.add(method);
@ -20,13 +17,11 @@ public:
(msg.add(args), ...); (msg.add(args), ...);
// отправить // отправить
channel.send(msg.str()); channel.send(msg);
// получить ответ // получить ответ
std::string resp = channel.receive(); IpcMessage resp = channel.receive();
IpcMessageReader reader(resp); return resp.template get<Ret>();
return reader.template read<Ret>();
} }
private: private:

@ -14,24 +14,24 @@ public:
const std::string& name, const std::string& name,
Ret (Obj::*method)(Args...)) { Ret (Obj::*method)(Args...)) {
handlers[name] = handlers[name] =
[instance, method](const std::string& req) -> std::string { [instance, method](const IpcMessage& req) -> IpcMessage {
IpcMessageReader reader(req); IpcMessage msg = req;
// пропустить имя метода // пропустить имя метода
(void)reader.template read<std::string>(); (void)msg.template get<std::string>();
// читать аргументы и вызвать метод // читать аргументы и вызвать метод
return callMethod<Ret, Obj, Args...>(instance, method, reader); return callMethod<Ret, Obj, Args...>(instance, method, msg);
}; };
} }
std::string dispatch(const std::string& request) { IpcMessage dispatch(const IpcMessage& request) {
IpcMessageReader reader(request); IpcMessage tmp = request;
const std::string method = reader.read<std::string>(); const std::string method = tmp.get<std::string>();
auto it = handlers.find(method); auto it = handlers.find(method);
if (it == handlers.end()) { if (it == handlers.end()) {
return "ERR"; return IpcMessage{};
} }
return it->second(request); return it->second(request);
@ -39,25 +39,25 @@ public:
private: private:
template<typename Ret, typename Obj, typename... Args> template<typename Ret, typename Obj, typename... Args>
static std::string callMethod(Obj* obj, static IpcMessage callMethod(Obj* obj,
Ret (Obj::*method)(Args...), Ret (Obj::*method)(Args...),
IpcMessageReader& reader) { IpcMessage& msg) {
auto tuple = readArgs<Args...>(reader); auto tuple = readArgs<Args...>(msg);
Ret result = Ret result =
std::apply(method, std::tuple_cat(std::make_tuple(obj), tuple)); std::apply(method, std::tuple_cat(std::make_tuple(obj), tuple));
IpcMessageWriter out; IpcMessage out;
out.add(result); out.add(result);
return out.str(); return out;
} }
template<typename... Args> template<typename... Args>
static std::tuple<Args...> readArgs(IpcMessageReader& reader) { static std::tuple<Args...> readArgs(IpcMessage& msg) {
return std::tuple<Args...>{reader.template read<Args>()...}; return std::tuple<Args...>{msg.template get<Args>()...};
} }
std::unordered_map<std::string, std::unordered_map<std::string,
std::function<std::string(const std::string&)>> std::function<IpcMessage(const IpcMessage&)>>
handlers; handlers;
}; };

@ -18,11 +18,11 @@ int main() {
MyServiceSkeleton skeleton(realObj); MyServiceSkeleton skeleton(realObj);
while (true) { while (true) {
std::string req = ch.receive(); IpcMessage req = ch.receive();
if (req.empty()) { if (req.empty()) {
break; break;
} }
std::string resp = skeleton.dispatch(req); IpcMessage resp = skeleton.dispatch(req);
ch.send(resp); ch.send(resp);
} }
} }

@ -9,7 +9,7 @@
{% endfor %} {% endfor %}
} }
std::string {{ cls.name }}Skeleton::dispatch(const std::string& req) { IpcMessage {{ cls.name }}Skeleton::dispatch(const IpcMessage& req) {
return invoker.dispatch(req); return invoker.dispatch(req);
} }

@ -3,13 +3,11 @@
#include "{{ cls.name }}.h" #include "{{ cls.name }}.h"
#include "rpc/RpcInvoker.h" #include "rpc/RpcInvoker.h"
#include <string>
class {{ cls.name }}Skeleton { class {{ cls.name }}Skeleton {
public: public:
explicit {{ cls.name }}Skeleton({{ cls.name }}& obj); explicit {{ cls.name }}Skeleton({{ cls.name }}& obj);
std::string dispatch(const std::string& req); IpcMessage dispatch(const IpcMessage& req);
private: private:
RpcInvoker invoker; RpcInvoker invoker;

Loading…
Cancel
Save