为何nng_dial仅在gtest环境中出现阻塞?
GTest环境下NNG的
nng_dial在第二个测试中阻塞的问题 问题描述
我在C++中使用NNG搭配GTest时遇到了一个疑难问题:独立编写的发布者程序可持续发送消息,订阅者程序也能成功订阅并接收消息,说明NNG的基础配置没有问题。但我编写的两个GTest测试中,第二个测试始终在nng_dial调用处阻塞——即便已尽力清理资源,nng_dial本应即时返回(即使连接失败)。我也尝试了创建dialer并启动的等效代码,问题依旧。请问为什么这个问题仅在GTest环境中出现?
相关代码
发布者程序代码
#include <nng/nng.h> #include <nng/protocol/pubsub0/pub.h> #include <iostream> #include <cstring> #include <string> #include <chrono> #include <thread> #define URL "ipc:///tmp/tc3-pub" int main() { nng_socket sock; int rv; // Open the socket if ((rv = nng_pub0_open(&sock)) != 0) { std::cout << "Failed to open nng socket\n"; return rv; } // Listen for subscribers if ((rv = nng_listen(sock, URL, NULL, 0)) != 0) { std::cout << "Failed to listen on nng socket\n"; return rv; } std::cout << "Socket is listening!\n"; for (int i = 0; i < 60; ++i) { std::string msg = "Message " + std::to_string(i); if ((rv = nng_send(sock, (void *) msg.c_str(), msg.size() + 1, 0)) != 0) { std::cout << "Failed to send message\n"; return rv; } std::cout << "Sent: " << msg << "\n"; // Sleep for one second std::this_thread::sleep_for(std::chrono::seconds(1)); } // Cleanup the socket nng_close(sock); return 0; }
订阅者程序代码
#include <nng/nng.h> #include <nng/protocol/pubsub0/sub.h> #include <nng/protocol/pubsub0/pub.h> #include <iostream> #include <cstring> #include <string> #include <thread> #define URL "ipc:///tmp/tc3-pub" int main() { nng_socket sock; int rv; // Open the socket if ((rv = nng_sub0_open(&sock)) != 0) { std::cerr << "Failed to open nng socket\n"; return rv; } nng_socket_set(sock, NNG_OPT_SUB_SUBSCRIBE, "", 0); std::cout << "Opened a socket!\n"; // Dial (connect to) the server if ((rv = nng_dial(sock, URL, NULL, 0)) != 0) { std::cerr << "Failed to dial on nng socket\n"; return rv; } // Receive and print messages continuously until an error occurs while (true) { char* buf = NULL; size_t size = 0; rv = nng_recv(sock, &buf, &size, NNG_FLAG_ALLOC); if (rv != 0) { std::cerr << "Failed to receive message\n"; break; } std::cout << "Received message: " << std::string(buf, size) << '\n'; nng_free(buf, size); } nng_close(sock); return rv; }
GTest测试代码
#include "gtest/gtest.h" #include <cstdlib> #include <filesystem> #include <condition_variable> #include <mutex> #include <thread> #include "nng/nng.h" #include "nng/protocol/pubsub0/pub.h" #include "nng/protocol/pubsub0/sub.h" TEST(NngTest, Test1) { nng_socket sock; int rv; if ((rv = nng_sub0_open(&sock)) != 0) { std::cout << "Failed to open nng socket\n"; } std::cout << "nng_sub0_open done\n"; nng_close(sock); nng_fini(); } TEST(NngTest, Test2) { nng_socket sock; int rv; if ((rv = nng_sub0_open(&sock)) != 0) { std::cout << "Failed to open nng socket\n"; } std::cout << "nng_sub0_open done\n"; if ((rv = nng_socket_set(sock, NNG_OPT_SUB_SUBSCRIBE, "", 0)) != 0) { std::cout << "Failed to set nng socket\n"; } std::cout << "nng_socket_set done\n"; if ((rv = nng_dial(sock, "ipc:///tmp/tc4-pub", NULL, 0)) != 0) { std::cout << "Failed to dial nng socket\n"; } std::cout << "nng_dial done\n"; nng_close(sock); nng_fini(); } int main(int argc, char** argv) { try { ::testing::InitGoogleTest(&argc, argv); int retval = RUN_ALL_TESTS(); std::cout << "Finished running all tests\n"; return retval; } catch (const std::exception& e) { std::cerr << "Caught exception: " << e.what() << '\n'; } catch (...) { std::cerr << "Caught unknown exception\n"; } return -1; }
问题原因与解决方法
核心原因
问题出在每个测试用例中都调用了nng_fini()。NNG的nng_fini()是用来销毁整个库的全局状态的,应该在进程退出前仅调用一次。而GTest的所有测试用例都运行在同一个进程空间内,第一个测试调用nng_fini()后,NNG的内部全局资源已经被销毁,第二个测试再调用NNG的API时,内部初始化状态异常,导致nng_dial的行为不符合预期(本该快速失败却阻塞)。
解决步骤
- 移除每个测试用例中的
nng_fini()调用; - 仅在整个测试程序结束时(比如
main函数最后)调用一次nng_fini(); - 确保每个测试用例中的NNG资源(如
sock)都通过nng_close()正确释放,避免资源泄漏。
修改后的GTest测试代码示例
#include "gtest/gtest.h" #include <cstdlib> #include <filesystem> #include <condition_variable> #include <mutex> #include <thread> #include "nng/nng.h" #include "nng/protocol/pubsub0/pub.h" #include "nng/protocol/pubsub0/sub.h" TEST(NngTest, Test1) { nng_socket sock; int rv; if ((rv = nng_sub0_open(&sock)) != 0) { std::cout << "Failed to open nng socket\n"; } std::cout << "nng_sub0_open done\n"; nng_close(sock); // 移除nng_fini() } TEST(NngTest, Test2) { nng_socket sock; int rv; if ((rv = nng_sub0_open(&sock)) != 0) { std::cout << "Failed to open nng socket\n"; } std::cout << "nng_sub0_open done\n"; if ((rv = nng_socket_set(sock, NNG_OPT_SUB_SUBSCRIBE, "", 0)) != 0) { std::cout << "Failed to set nng socket\n"; } std::cout << "nng_socket_set done\n"; if ((rv = nng_dial(sock, "ipc:///tmp/tc4-pub", NULL, 0)) != 0) { std::cout << "Failed to dial nng socket\n"; } std::cout << "nng_dial done\n"; nng_close(sock); // 移除nng_fini() } int main(int argc, char** argv) { try { ::testing::InitGoogleTest(&argc, argv); int retval = RUN_ALL_TESTS(); std::cout << "Finished running all tests\n"; nng_fini(); // 所有测试结束后统一调用一次 return retval; } catch (const std::exception& e) { std::cerr << "Caught exception: " << e.what() << '\n'; } catch (...) { std::cerr << "Caught unknown exception\n"; } nng_fini(); // 异常场景也确保调用清理 return -1; }
补充说明
独立运行的发布者/订阅者程序中,nng_fini()在进程结束前被隐式或显式调用一次,所以不会有问题。但GTest的多测试用例共享进程空间,重复调用nng_fini()会破坏NNG的全局状态,导致后续API行为异常,这就是问题仅在GTest环境中出现的根本原因。
内容的提问来源于stack exchange,提问作者Kevin
相关产品推荐
相关产品推荐

