This commit is contained in:
Сергей Маринкевич
2025-12-02 19:18:36 +07:00
parent 4433568545
commit 785fca07f1
15 changed files with 140 additions and 62 deletions
+15
View File
@@ -0,0 +1,15 @@
#pragma once
#include "IpcMessage.h"
// Абстракция IPC‑канала, работающего с IpcMessage.
// Живёт отдельно от RPC‑кода, чтобы транспорт не зависел от конкретной RPC‑реализации.
class IpcChannel {
public:
virtual ~IpcChannel() = default;
virtual void send(const IpcMessage& msg) = 0;
virtual IpcMessage receive() = 0;
};
+106
View File
@@ -0,0 +1,106 @@
#pragma once
#include <sstream>
#include <string>
// Примитивное IPC‑сообщение с API add<T>() / get<T>().
// Под капотом пока текстовый формат с типовыми тегами, но снаружи интерфейс не завязан на std::string.
class IpcMessage {
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>
void add(const T& v);
// Чтение входящего сообщения по частям.
template<typename T>
T get();
const std::string& raw() const {
return raw_;
}
bool empty() const {
return raw_.empty();
}
private:
std::string raw_;
std::ostringstream out_;
std::istringstream in_;
bool in_initialized_{false};
void ensureInput() {
if (!in_initialized_) {
in_.str(raw_);
in_initialized_ = true;
}
}
};
// ===== Специализации по типам =====
// int
template<>
inline void IpcMessage::add<int>(const int& v) {
out_ << 'i' << ' ' << v << ' ';
raw_ = out_.str();
}
template<>
inline int IpcMessage::get<int>() {
ensureInput();
char tag{};
in_ >> tag;
// в PoC просто доверяем, что тип совпадает
int v{};
in_ >> v;
return v;
}
// std::string
template<>
inline void IpcMessage::add<std::string>(const std::string& v) {
// формат: 's' <len> <bytes...>
out_ << 's' << ' ' << v.size() << ' ';
out_.write(v.data(), static_cast<std::streamsize>(v.size()));
out_ << ' ';
raw_ = out_.str();
}
template<>
inline std::string IpcMessage::get<std::string>() {
ensureInput();
char tag{};
in_ >> tag;
// ожидаем 's'
std::size_t len{};
in_ >> len;
// съесть одиночный пробел перед данными
in_.get();
std::string res(len, '\0');
in_.read(&res[0], static_cast<std::streamsize>(len));
// съесть завершающий пробел
in_.get();
return res;
}
+57
View File
@@ -0,0 +1,57 @@
#pragma once
#include "IpcChannel.h"
#include <fcntl.h>
#include <sys/stat.h>
#include <unistd.h>
// IPC‑канал поверх именованных pipe.
// Инкапсулирует работу с файловыми дескрипторами и обмен сообщениями IpcMessage.
// readPipe — тот FIFO, который этот endpoint читает; writePipe — тот, в который пишет.
class IpcPipeChannel : public IpcChannel {
public:
IpcPipeChannel(const char* readPipe, const char* writePipe) {
// Канал не создаёт FIFO, только открывает.
// Открываем оба конца как O_RDWR, чтобы избежать блокировок на open(O_RDONLY/O_WRONLY).
// При этом логически читаем только из readPipe, а пишем только в writePipe.
fdIn_ = ::open(readPipe, O_RDWR);
fdOut_ = ::open(writePipe, O_RDWR);
}
~IpcPipeChannel() override {
if (fdIn_ >= 0) {
::close(fdIn_);
}
if (fdOut_ >= 0) {
::close(fdOut_);
}
}
void send(const IpcMessage& msg) override {
if (fdOut_ < 0) {
return;
}
const std::string& data = msg.raw();
::write(fdOut_, data.c_str(), data.size());
::write(fdOut_, "\n", 1);
}
IpcMessage receive() override {
if (fdIn_ < 0) {
return IpcMessage{};
}
char buf[4096];
const int n = ::read(fdIn_, buf, sizeof(buf) - 1);
if (n <= 0) {
return IpcMessage{};
}
buf[n] = 0;
return IpcMessage(std::string(buf));
}
private:
int fdIn_{-1};
int fdOut_{-1};
};