#pragma once #include "IpcChannel.h" #include #include #include #include #include // 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); // Проверяем, что каналы открыты успешно if (fdIn_ < 0) { std::cerr << "Failed to open read pipe: " << readPipe << " (errno: " << errno << ")" << std::endl; } if (fdOut_ < 0) { std::cerr << "Failed to open write pipe: " << writePipe << " (errno: " << errno << ")" << std::endl; } // Очищаем канал для чтения от остаточных данных // Это важно, чтобы избежать чтения старых сообщений при повторном открытии канала if (fdIn_ >= 0) { char buf[4096]; // Устанавливаем неблокирующий режим для очистки int flags = ::fcntl(fdIn_, F_GETFL); ::fcntl(fdIn_, F_SETFL, flags | O_NONBLOCK); // Читаем все доступные данные (старые сообщения) while (::read(fdIn_, buf, sizeof(buf)) > 0) { // Просто выбрасываем старые данные } // Возвращаем блокирующий режим ::fcntl(fdIn_, F_SETFL, flags); } } ~IpcPipeChannel() override { if (fdIn_ >= 0) { ::close(fdIn_); } if (fdOut_ >= 0) { ::close(fdOut_); } } void send(const IpcMessage& msg) override { if (fdOut_ < 0) { return; } auto raw = msg.serialize(); // тип определяется сериализатором // Для текстовых форматов (std::string): if constexpr (std::is_same_v) { const std::string& data = raw; ::write(fdOut_, data.c_str(), data.size()); ::write(fdOut_, "\n", 1); } // Можно добавить другие форматы по мере необходимости } IpcMessage receive() override { if (fdIn_ < 0) { return IpcMessage{}; } // Для текстовых форматов: if constexpr (std::is_same_v) { std::string line; char c; // Читаем построчно до символа новой строки while (true) { const int n = ::read(fdIn_, &c, 1); if (n < 0) { // Ошибка чтения if (errno == EAGAIN || errno == EWOULDBLOCK) { // Неблокирующий режим и нет данных - это нормально, возвращаем пустое сообщение return IpcMessage{}; } // Другая ошибка - логируем и возвращаем пустое сообщение std::cerr << "read() error: " << errno << std::endl; return IpcMessage{}; } if (n == 0) { // EOF - канал закрыт на стороне записи или нет открытых дескрипторов записи // Для именованных каналов это может означать, что клиент еще не открыл канал для записи // В этом случае read() должен блокироваться, но если канал открыт в O_RDWR, // то read() может вернуть 0 немедленно // Если мы уже прочитали что-то, возвращаем это, иначе пустое сообщение if (line.empty()) { return IpcMessage{}; } break; // EOF, но есть данные - возвращаем их } if (c == '\n') { break; // Достигли конца строки } line += c; } if (line.empty()) { return IpcMessage{}; } return IpcMessage(line); } // Можно добавить другие форматы по мере необходимости return IpcMessage{}; } private: int fdIn_{-1}; int fdOut_{-1}; };