From 3342020ecf181474c479262a1aa98a97818d724e Mon Sep 17 00:00:00 2001 From: hukai Date: Wed, 30 Jul 2025 09:38:43 +0800 Subject: [PATCH] first commit --- .gitignore | 9 +++ README.md | 24 ++++++ include/context.h | 31 +++++++ include/core.h | 33 ++++++++ include/shm.h | 36 +++++++++ include/tcp.h | 19 +++++ include/unix_domain.h | 23 ++++++ include/zmq_ipc.h | 19 +++++ src/core.cpp | 0 src/main.cpp | 87 ++++++++++++++++++++ src/shm.cpp | 161 +++++++++++++++++++++++++++++++++++++ src/tcp.cpp | 91 +++++++++++++++++++++ src/unix_domain.cpp | 139 ++++++++++++++++++++++++++++++++ src/zmq_ipc.cpp | 27 +++++++ tests/test_shm.cpp | 12 +++ tests/test_unix_domain.cpp | 11 +++ tests/test_zmq.cpp | 12 +++ tests/utils.hpp | 48 +++++++++++ xmake.lua | 104 ++++++++++++++++++++++++ 19 files changed, 886 insertions(+) create mode 100644 .gitignore create mode 100644 README.md create mode 100644 include/context.h create mode 100644 include/core.h create mode 100644 include/shm.h create mode 100644 include/tcp.h create mode 100644 include/unix_domain.h create mode 100644 include/zmq_ipc.h create mode 100644 src/core.cpp create mode 100644 src/main.cpp create mode 100644 src/shm.cpp create mode 100644 src/tcp.cpp create mode 100644 src/unix_domain.cpp create mode 100644 src/zmq_ipc.cpp create mode 100644 tests/test_shm.cpp create mode 100644 tests/test_unix_domain.cpp create mode 100644 tests/test_zmq.cpp create mode 100644 tests/utils.hpp create mode 100644 xmake.lua diff --git a/.gitignore b/.gitignore new file mode 100644 index 0000000..4eed1f4 --- /dev/null +++ b/.gitignore @@ -0,0 +1,9 @@ +# Xmake cache +.xmake/ +build/ + +# MacOS Cache +.DS_Store + + +.vscode/ \ No newline at end of file diff --git a/README.md b/README.md new file mode 100644 index 0000000..2367ff6 --- /dev/null +++ b/README.md @@ -0,0 +1,24 @@ +# Node Communication + +## 准备 + +```shell +xmake f -m release -cv +``` + +## 运行 + +### TCP + +1. 接收端执行 `xmake run ipc_interface tcp receive` +1. 发送端执行 `xmake run ipc_interface tcp send --data test_data` + +### Share Memory + +1. 接收端执行 `xmake run ipc_interface shm receive` +1. 发送端执行 `xmake run ipc_interface shm send --data test_data` + +### Unix Domain + +1. 接收端执行 `xmake run ipc_interface unix_domain receive` +1. 发送端执行 `xmake run ipc_interface unix_domain send --data test_data` diff --git a/include/context.h b/include/context.h new file mode 100644 index 0000000..e46dcd7 --- /dev/null +++ b/include/context.h @@ -0,0 +1,31 @@ +#pragma once +#include "core.h" +#include + +/** + * @brief + * + */ +class IpcRequestReplyContext { + private: + std::unique_ptr strategy; + + public: + void setStrategy(std::unique_ptr newStrategy) { + strategy = std::move(newStrategy); + } + + std::expected send(const std::string& message) { + if (strategy) { + return strategy->send(message); + } + return std::unexpected("Not Found Strategy"); + } + + std::expected receive() { + if (strategy) { + return strategy->receive(); + } + return std::unexpected("Not Found Strategy"); + } +}; \ No newline at end of file diff --git a/include/core.h b/include/core.h new file mode 100644 index 0000000..837568f --- /dev/null +++ b/include/core.h @@ -0,0 +1,33 @@ +#pragma once +#include +#include + +/** + * @brief 发布,订阅 + * + */ +class IpcPubSubInterface { + public: + virtual ~IpcPubSubInterface() = default; + + virtual std::expected publish( + const std::string& topic, const std::string& message) = 0; + virtual std::expected subscribe( + const std::string& topic) = 0; +}; + +/** + * @brief 请求,回复 + * + */ +class IpcRequestReplyInterface { + public: + virtual ~IpcRequestReplyInterface() = default; + + virtual std::expected send(const std::string& message) = 0; + virtual std::expected receive() = 0; +}; + + +// 缓冲区大小 +constexpr size_t BUFFER_SIZE = 4096; diff --git a/include/shm.h b/include/shm.h new file mode 100644 index 0000000..ac0f251 --- /dev/null +++ b/include/shm.h @@ -0,0 +1,36 @@ +#pragma once +#include "core.h" + +/** + * @brief 共享内存 请求,回复 实现 + * + */ +class ShmRequestReply : public IpcRequestReplyInterface { + public: + ShmRequestReply(); + ~ShmRequestReply(); + + std::expected receive() override; + std::expected send(const std::string& message) override; +}; + +/** + * @brief 共享内存 发布,订阅 实现 + * + */ +class ShmPubSub : public IpcPubSubInterface { + public: + ShmPubSub(); + ~ShmPubSub(); + + std::expected publish(const std::string& topic, + const std::string& message) override; + std::expected subscribe(const std::string& topic) override; +}; + + +/** + * @brief 共享内存 请求,回复 地址 + * + */ +constexpr const char* SHM_REQUEST_REPLY_NAME = "/shm_req"; diff --git a/include/tcp.h b/include/tcp.h new file mode 100644 index 0000000..beba761 --- /dev/null +++ b/include/tcp.h @@ -0,0 +1,19 @@ +#pragma once +#include "core.h" + +/** + * @brief TCP 请求与回复 实现 + * + */ +class TcpRequestReply : public IpcRequestReplyInterface { + public: + TcpRequestReply(); + ~TcpRequestReply(); + + std::expected receive() override; + std::expected send(const std::string& message) override; +}; + + +constexpr int PORT = 12345; +constexpr const char* LOCALHOST = "127.0.0.1"; \ No newline at end of file diff --git a/include/unix_domain.h b/include/unix_domain.h new file mode 100644 index 0000000..505d172 --- /dev/null +++ b/include/unix_domain.h @@ -0,0 +1,23 @@ +#pragma once +#include "core.h" + +/** + * @brief unix-domain 请求,回复 实现 + * + */ +class UnixDomainRequestReply : public IpcRequestReplyInterface { + public: + UnixDomainRequestReply(); + ~UnixDomainRequestReply(); + + std::expected receive() override; + std::expected send(const std::string& message) override; +}; + +// unix-domain 地址 +constexpr const char* SOCKET_PATH = "/tmp/unix_domain_socket"; + +// 默认重试次数 +const int RETRY_COUNT = 10; +// 重试间隔(毫秒) +const int RETRY_DELAY_MS = 500; \ No newline at end of file diff --git a/include/zmq_ipc.h b/include/zmq_ipc.h new file mode 100644 index 0000000..753216b --- /dev/null +++ b/include/zmq_ipc.h @@ -0,0 +1,19 @@ +#pragma once +#include "core.h" +#include + + +class ZMQRequestReply : public IpcRequestReplyInterface { + public: + ZMQRequestReply(); + ~ZMQRequestReply(); + + std::expected receive() override; + std::expected send(const std::string& message) override; + + private: + zmq::context_t ctx_; + zmq::socket_t receiver_; + zmq::socket_t sender_; + static constexpr const char* endpoint_ = "inproc://test"; +}; diff --git a/src/core.cpp b/src/core.cpp new file mode 100644 index 0000000..e69de29 diff --git a/src/main.cpp b/src/main.cpp new file mode 100644 index 0000000..d6d333c --- /dev/null +++ b/src/main.cpp @@ -0,0 +1,87 @@ +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +int parse_cmd_args(int argc, char** argv, std::string& communication, + std::string& method, std::string& data) { + argparse::ArgumentParser program("IPC_Interface"); + + program.add_argument("communication") + .help("zmq or shm or unix_domain") + .store_into(communication); + + program.add_argument("method").help("send or receive").store_into(method); + + program.add_argument("--data").default_value(std::string("")).nargs(1); + + try { + program.parse_args(argc, argv); + } catch (const std::exception& err) { + std::cerr << err.what() << std::endl; + std::cerr << program; + return 1; + } + + if (method == "send") { + std::cout << method << std::endl; + } + data = program.get("--data"); + return 0; +} + +int run_main(int argc, char** argv) { + std::string communication; + std::string method; + std::string data; + + if (auto res = parse_cmd_args(argc, argv, communication, method, data); + res != 0) { + return res; + } + + IpcRequestReplyContext context; + + // 使用共享内存策略 + if (communication == "zmq") { + context.setStrategy(std::make_unique()); + } else if (communication == "shm") { + context.setStrategy(std::make_unique()); + } else if (communication == "unix_domain") { + context.setStrategy(std::make_unique()); + } else if (communication == "tcp") { + context.setStrategy(std::make_unique()); + } + + if (method == "send") { + if (!data.empty()) { + auto res = context.send(data); + if (!res.has_value()) { + std::cerr << "send error: " << res.error() << std::endl; + return -1; + } + std::cout << "send success" << std::endl; + } + + } else if (method == "receive") { + auto read_msg = context.receive(); + if (!read_msg.has_value()) { + std::cerr << "receive error: " << read_msg.error() << std::endl; + return -1; + } + std::cout << "Received: " << *read_msg << std::endl; + } + + return 0; +} + +int main(int argc, char** argv) { + return run_main(argc, argv); + // return 0; +} \ No newline at end of file diff --git a/src/shm.cpp b/src/shm.cpp new file mode 100644 index 0000000..c8e8564 --- /dev/null +++ b/src/shm.cpp @@ -0,0 +1,161 @@ +#include +#include +#include + +#include +#include +#include + +#include "shm.h" + + +/** + * @brief Construct a new Shm Request Reply:: Shm Request Reply object + * + */ +ShmRequestReply::ShmRequestReply() { + // 初始化共享内存(创建端或者连接端) + // 此处省略具体实现,可用 shm_open + mmap 等 +} + +/** + * @brief Destroy the Shm Request Reply:: Shm Request Reply object + * + */ +ShmRequestReply::~ShmRequestReply() { + // 清理共享内存资源 +} + +/** + * @brief 接收数据 + * + * @return std::expected + */ +std::expected ShmRequestReply::receive() { + // 以只读方式打开请求共享内存区域 + int fd = shm_open(SHM_REQUEST_REPLY_NAME, O_RDONLY, 0666); + if (fd == -1) + return std::unexpected("shm_open failed: " + std::string(strerror(errno))); + // 将共享内存映射到进程地址空间 + void* ptr = mmap(nullptr, BUFFER_SIZE, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) return std::unexpected("mmap failed"); + // 将共享内存内容转换为字符串 + std::string data(static_cast(ptr)); + // 清理资源:取消映射、关闭文件描述符 + munmap(ptr, BUFFER_SIZE); + close(fd); + // 返回读取到的数据 + return data; +} + +/** + * @brief 发送数据 + * + * @param message + * @return std::expected + */ +std::expected ShmRequestReply::send( + const std::string& message) { + // 创建或打开响应共享内存区域(可读写) + int fd = shm_open(SHM_REQUEST_REPLY_NAME, O_CREAT | O_RDWR, 0666); + if (fd == -1) + return std::unexpected("shm_open failed: " + std::string(strerror(errno))); + // 调整共享内存大小 + auto res = ftruncate(fd, BUFFER_SIZE); + if (res != 0) { + return std::unexpected("ftruncate failed: " + std::string(strerror(errno))); + } + // 将共享内存映射到进程地址空间(可写) + void* ptr = mmap(nullptr, BUFFER_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) return std::unexpected("mmap failed"); + // 清空共享内存区域 + std::memset(ptr, 0, BUFFER_SIZE); + // 将消息内容拷贝到共享内存(确保不超过大小限制) + std::memcpy(ptr, message.data(), std::min(message.size(), BUFFER_SIZE - 1)); + // 清理资源:取消映射、关闭文件描述符 + munmap(ptr, BUFFER_SIZE); + close(fd); + return {}; +} + +/** + * @brief 构造topic + * + * @param topic + * @return std::string + */ +std::string make_topic_name(const std::string& topic) { + return "/shm_topic_" + topic; +} + +/** + * @brief Construct a new Shm Pub Sub:: Shm Pub Sub object + * + */ +ShmPubSub::ShmPubSub() { + // 初始化 +} + +/** + * @brief Destroy the Shm Pub Sub:: Shm Pub Sub object + * + */ +ShmPubSub::~ShmPubSub() { + // 清理 +} + +/** + * @brief 发布 + * + * @param topic + * @param message + * @return std::expected + */ +std::expected ShmPubSub::publish( + const std::string& topic, const std::string& message) { + auto shm_name = make_topic_name(topic); + // 创建或打开响应共享内存区域(可读写) + int fd = shm_open(shm_name.c_str(), O_CREAT | O_RDWR, 0666); + if (fd == -1) + return std::unexpected("shm_open failed: " + std::string(strerror(errno))); + // 调整共享内存大小 + auto res = ftruncate(fd, BUFFER_SIZE); + if (res != 0) { + return std::unexpected("ftruncate failed: " + std::string(strerror(errno))); + } + // 将共享内存映射到进程地址空间(可写) + void* ptr = mmap(nullptr, BUFFER_SIZE, PROT_WRITE, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) return std::unexpected("mmap failed"); + // 清空共享内存区域 + std::memset(ptr, 0, BUFFER_SIZE); + std::memcpy(ptr, message.data(), std::min(message.size(), BUFFER_SIZE - 1)); + // 清理资源:取消映射、关闭文件描述符 + munmap(ptr, BUFFER_SIZE); + close(fd); + return {}; +} + +/** + * @brief 订阅 + * + * @param topic + * @return std::expected + */ +std::expected ShmPubSub::subscribe( + const std::string& topic) { + auto shm_name = make_topic_name(topic); + // 以只读方式打开请求共享内存区域 + int fd = shm_open(shm_name.c_str(), O_RDONLY, 0666); + if (fd == -1) + return std::unexpected("shm_open failed: " + std::string(strerror(errno))); + // 将共享内存映射到进程地址空间 + void* ptr = mmap(nullptr, BUFFER_SIZE, PROT_READ, MAP_SHARED, fd, 0); + if (ptr == MAP_FAILED) return std::unexpected("mmap failed"); + // 将共享内存内容转换为字符串 + std::string data(static_cast(ptr)); + std::cout << "Received [" << topic << "]: " << data << "\n"; + // 清理资源:取消映射、关闭文件描述符 + munmap(ptr, BUFFER_SIZE); + close(fd); + return {}; +} \ No newline at end of file diff --git a/src/tcp.cpp b/src/tcp.cpp new file mode 100644 index 0000000..496cd8c --- /dev/null +++ b/src/tcp.cpp @@ -0,0 +1,91 @@ +#include +#include +#include +#include + +#include +#include +#include + +#include "tcp.h" + + + +TcpRequestReply::TcpRequestReply() {} + +TcpRequestReply::~TcpRequestReply() {} + +std::expected TcpRequestReply::receive() { + int server_fd = socket(AF_INET, SOCK_STREAM, 0); + if (server_fd < 0) { + return std::unexpected("socket() failed"); + } + + sockaddr_in server_addr{}; + server_addr.sin_family = AF_INET; + server_addr.sin_addr.s_addr = INADDR_ANY; + server_addr.sin_port = htons(PORT); + + int opt = 1; + setsockopt(server_fd, SOL_SOCKET, SO_REUSEADDR, &opt, sizeof(opt)); + + if (bind(server_fd, reinterpret_cast(&server_addr), + sizeof(server_addr)) < 0) { + close(server_fd); + return std::unexpected("bind() failed"); + } + + if (listen(server_fd, 1) < 0) { + close(server_fd); + return std::unexpected("listen() failed"); + } + + int client_fd = accept(server_fd, nullptr, nullptr); + if (client_fd < 0) { + close(server_fd); + return std::unexpected("accept() failed"); + } + + char buffer[BUFFER_SIZE] = {0}; + ssize_t bytes_read = read(client_fd, buffer, BUFFER_SIZE); + if (bytes_read < 0) { + close(client_fd); + close(server_fd); + return std::unexpected("read() failed"); + } + + close(client_fd); + close(server_fd); + return std::string(buffer, static_cast(bytes_read)); +} + +std::expected TcpRequestReply::send( + const std::string& message) { + int sock = socket(AF_INET, SOCK_STREAM, 0); + if (sock < 0) { + return std::unexpected("socket() failed"); + } + + sockaddr_in server_addr{}; + server_addr.sin_family = AF_INET; + server_addr.sin_port = htons(PORT); + + if (inet_pton(AF_INET, LOCALHOST, &server_addr.sin_addr) <= 0) { + close(sock); + return std::unexpected("inet_pton() failed"); + } + + if (connect(sock, reinterpret_cast(&server_addr), + sizeof(server_addr)) < 0) { + close(sock); + return std::unexpected("connect() failed"); + } + + if (write(sock, message.c_str(), message.size()) < 0) { + close(sock); + return std::unexpected("write() failed"); + } + + close(sock); + return {}; +} diff --git a/src/unix_domain.cpp b/src/unix_domain.cpp new file mode 100644 index 0000000..0019a35 --- /dev/null +++ b/src/unix_domain.cpp @@ -0,0 +1,139 @@ +#include +#include +#include + +#include +#include +#include +#include +#include + +#include "unix_domain.h" + +/** + * @brief Construct a new Unix Domain Request Reply:: Unix Domain Request Reply + * object + * + */ +UnixDomainRequestReply::UnixDomainRequestReply() {} + +/** + * @brief Destroy the Unix Domain Request Reply:: Unix Domain Request Reply + * object + * + */ +UnixDomainRequestReply::~UnixDomainRequestReply() { + unlink(SOCKET_PATH); // 清理 socket 文件 +} + +/** + * @brief 接收消息 + * + * @return std::expected + */ +std::expected UnixDomainRequestReply::receive() { + // 1. 创建UNIX域流式socket + int server_fd = socket(AF_UNIX, SOCK_STREAM, 0); + if (server_fd < 0) { + return std::unexpected("创建socket失败: " + std::string(strerror(errno))); + } + + // 2. 绑定socket到指定路径 + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1); + + // 确保之前的socket文件被删除 + unlink(SOCKET_PATH); + + if (bind(server_fd, (sockaddr*)&addr, sizeof(addr)) < 0) { + close(server_fd); + return std::unexpected("绑定socket失败: " + std::string(strerror(errno))); + } + + // 3. 开始监听连接(队列长度为1) + if (listen(server_fd, 1) < 0) { + close(server_fd); + return std::unexpected("监听失败: " + std::string(strerror(errno))); + } + + // 4. 接受客户端连接 + int client_fd = accept(server_fd, nullptr, nullptr); + if (client_fd < 0) { + close(server_fd); + return std::unexpected("接受连接失败: " + std::string(strerror(errno))); + } + + // 5. 读取客户端数据 + char buffer[BUFFER_SIZE] = {0}; + ssize_t bytes_read = read(client_fd, buffer, sizeof(buffer)); + + // 6. 关闭连接(使用RAII会更好) + close(client_fd); + close(server_fd); + + if (bytes_read < 0) { + return std::unexpected("读取数据失败: " + std::string(strerror(errno))); + } + + return std::string(buffer, bytes_read); +} + +/** + * @brief 发送消息(一般要先接收,在发送,这里做了轮询尝试,可以将他反过来) + * + * @param message + * @return std::expected + */ +std::expected UnixDomainRequestReply::send( + const std::string& message) { + int sock = -1; + + // 1. 创建socket(放在循环外避免重复创建) + sock = socket(AF_UNIX, SOCK_STREAM, 0); + if (sock < 0) { + return std::unexpected("创建socket失败: " + std::string(strerror(errno))); + } + + // 2. 准备地址结构 + sockaddr_un addr{}; + addr.sun_family = AF_UNIX; + strncpy(addr.sun_path, SOCKET_PATH, sizeof(addr.sun_path) - 1); + + // 3. 带重试的连接逻辑 + bool connected = false; + for (int attempt = 0; attempt < RETRY_COUNT; ++attempt) { + if (connect(sock, (sockaddr*)&addr, sizeof(addr)) == 0) { + connected = true; + break; + } + + // 最后一次尝试不等待 + if (attempt < RETRY_COUNT - 1) { + std::this_thread::sleep_for(std::chrono::milliseconds(RETRY_DELAY_MS)); + } + } + + if (!connected) { + close(sock); + return std::unexpected("连接服务端失败: " + std::string(strerror(errno))); + } + + // 4. 设置发送超时 + timeval timeout{.tv_sec = 5, .tv_usec = 0}; + if (setsockopt(sock, SOL_SOCKET, SO_SNDTIMEO, &timeout, sizeof(timeout))) { + close(sock); + return std::unexpected("设置超时失败: " + std::string(strerror(errno))); + } + + // 3. 发送数据 + ssize_t bytes_sent = write(sock, message.c_str(), message.size()); + if (bytes_sent < 0) { + close(sock); + return std::unexpected("发送数据失败: " + std::string(strerror(errno))); + } + + // 4. 关闭连接 + close(sock); + return {}; +} \ No newline at end of file diff --git a/src/zmq_ipc.cpp b/src/zmq_ipc.cpp new file mode 100644 index 0000000..ae4ef37 --- /dev/null +++ b/src/zmq_ipc.cpp @@ -0,0 +1,27 @@ + + +#include "zmq_ipc.h" + +ZMQRequestReply::ZMQRequestReply() + : ctx_(1), // 1个IO线程 + receiver_(ctx_, zmq::socket_type::pull), + sender_(ctx_, zmq::socket_type::push) { + // 绑定接收端 + receiver_.bind(endpoint_); + // 连接发送端 + sender_.connect(endpoint_); +} + +ZMQRequestReply::~ZMQRequestReply() {} + +std::expected ZMQRequestReply::receive() { + zmq::message_t msg; + auto res = receiver_.recv(msg, zmq::recv_flags::none); + return std::string(msg.to_string()); +} + +std::expected ZMQRequestReply::send( + const std::string& message) { + sender_.send(zmq::buffer(message), zmq::send_flags::none); + return {}; +} \ No newline at end of file diff --git a/tests/test_shm.cpp b/tests/test_shm.cpp new file mode 100644 index 0000000..f34073f --- /dev/null +++ b/tests/test_shm.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + + +INSTANTIATE_TYPED_TEST_SUITE_P(ShmIPCTest, IPCTest, ShmRequestReply); + + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/test_unix_domain.cpp b/tests/test_unix_domain.cpp new file mode 100644 index 0000000..22f0e4b --- /dev/null +++ b/tests/test_unix_domain.cpp @@ -0,0 +1,11 @@ +#include +#include + +#include + +INSTANTIATE_TYPED_TEST_SUITE_P(UnixDomainTest, IPCTest, UnixDomainRequestReply); + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/test_zmq.cpp b/tests/test_zmq.cpp new file mode 100644 index 0000000..fec3024 --- /dev/null +++ b/tests/test_zmq.cpp @@ -0,0 +1,12 @@ +#include +#include +#include + + +INSTANTIATE_TYPED_TEST_SUITE_P(ZMQIPCTest, IPCTest,ZMQRequestReply); + + +int main(int argc, char** argv) { + testing::InitGoogleTest(&argc, argv); + return RUN_ALL_TESTS(); +} \ No newline at end of file diff --git a/tests/utils.hpp b/tests/utils.hpp new file mode 100644 index 0000000..603efe5 --- /dev/null +++ b/tests/utils.hpp @@ -0,0 +1,48 @@ +#include +#include +#include + +#include + +template +class IPCTest : public testing::Test { + public: + void send(auto msg) { + IpcRequestReplyContext ctx; + + ctx.setStrategy(std::make_unique()); + auto res = ctx.send(msg); + if (!res) FAIL() << "send failed!" << res.error(); + } + + void receive(auto msg) { + IpcRequestReplyContext ctx; + + ctx.setStrategy(std::make_unique()); + auto res = ctx.receive(); + if (!res) FAIL() << "receive failed!" << res.error(); + EXPECT_EQ(res, msg); + } +}; + +// +++++++++++++++++++++++++++++++++ Test +++++++++++++++++++++++++++++++++ +TYPED_TEST_SUITE_P(IPCTest); +// 单线程发送接收 +TYPED_TEST_P(IPCTest, SendAndReceiveTest) { + auto msg = "Hello IPC"; + this->send(msg); + this->receive(msg); +} + +// 多线程发送接收 +TYPED_TEST_P(IPCTest, MutliThreadSendAndReceiveTest) { + auto msg = "Hello IPC"; + std::thread send_t([this, msg]() { this->send(msg); }); + std::thread receive_t([this, msg]() { this->receive(msg); }); + + send_t.join(); + receive_t.join(); +} + +REGISTER_TYPED_TEST_SUITE_P(IPCTest, SendAndReceiveTest, + MutliThreadSendAndReceiveTest); diff --git a/xmake.lua b/xmake.lua new file mode 100644 index 0000000..80abf43 --- /dev/null +++ b/xmake.lua @@ -0,0 +1,104 @@ +add_rules("mode.debug", "mode.release") + +-- c++ 23 标准 +add_languages("c++23") +-- 头文件目录 +add_includedirs("include") +-- GoogleTest +add_requires("gtest") +-- cpp-httplib +add_requires("cpp-httplib") +-- zmq +add_requires("cppzmq") +-- argparse +add_requires("argparse") + +target("ipc_interface") + set_kind("binary") + add_files("src/*.cpp") + add_packages("cpp-httplib") + add_packages("cppzmq") + add_packages("argparse") + + +for _, file in ipairs(os.files("tests/test_*.cpp")) do + local name = path.basename(file) + target(name) + set_kind("binary") + add_packages("gtest") + add_packages("cpp-httplib") + add_packages("cppzmq") + set_default(false) + add_includedirs("tests") + add_files(file,"src/*.cpp|main.cpp") + add_tests("default") +end +-- +-- If you want to known more usage about xmake, please see https://xmake.io +-- +-- ## FAQ +-- +-- You can enter the project directory firstly before building project. +-- +-- $ cd projectdir +-- +-- 1. How to build project? +-- +-- $ xmake +-- +-- 2. How to configure project? +-- +-- $ xmake f -p [macosx|linux|iphoneos ..] -a [x86_64|i386|arm64 ..] -m [debug|release] +-- +-- 3. Where is the build output directory? +-- +-- The default output directory is `./build` and you can configure the output directory. +-- +-- $ xmake f -o outputdir +-- $ xmake +-- +-- 4. How to run and debug target after building project? +-- +-- $ xmake run [targetname] +-- $ xmake run -d [targetname] +-- +-- 5. How to install target to the system directory or other output directory? +-- +-- $ xmake install +-- $ xmake install -o installdir +-- +-- 6. Add some frequently-used compilation flags in xmake.lua +-- +-- @code +-- -- add debug and release modes +-- add_rules("mode.debug", "mode.release") +-- +-- -- add macro definition +-- add_defines("NDEBUG", "_GNU_SOURCE=1") +-- +-- -- set warning all as error +-- set_warnings("all", "error") +-- +-- -- set language: c99, c++11 +-- set_languages("c99", "c++11") +-- +-- -- set optimization: none, faster, fastest, smallest +-- set_optimize("fastest") +-- +-- -- add include search directories +-- add_includedirs("/usr/include", "/usr/local/include") +-- +-- -- add link libraries and search directories +-- add_links("tbox") +-- add_linkdirs("/usr/local/lib", "/usr/lib") +-- +-- -- add system link libraries +-- add_syslinks("z", "pthread") +-- +-- -- add compilation and link flags +-- add_cxflags("-stdnolib", "-fno-strict-aliasing") +-- add_ldflags("-L/usr/local/lib", "-lpthread", {force = true}) +-- +-- @endcode +-- +