C语言 发布-订阅方案中的异常处理(ZeroMQ)

xoshrz7s  于 9个月前  发布在  其他
关注(0)|答案(3)|浏览(95)

我已经用ZeroMQ创建了一个订阅者-订阅者通信方案,我注意到我的服务器和客户端程序有一个小问题。我知道在C中没有try catch(根据我的简短研究),但是在接下来的两个while(1)中没有异常捕获对我来说似乎是危险的。
考虑到下面的代码片段,处理异常(在while中)的最正确方法是什么?使用我现在的结构(正如你在下面看到的),zmq_closezmq_ctx_destroy将永远不会执行,但我希望它们在程序错误/异常(无论是什么原因)的情况下执行。

注意:在此架构中,我有一个客户端监听多个发布者,因此Client代码中的for周期。
服务器

(...inside main)

while (1) {
    char update[20];
    sprintf(update, "%s", "new_update");
    s_send(publisher, update);
    sleep(1);
}

zmq_close(publisher);
zmq_ctx_destroy(context);
return 0;

客户端

(...inside main)

while(1){
    for (c = 1; c < server_num; c = c + 1){
        char *msg = s_recv(subscribers[c]);

        if (msg) {
            printf("%s\n",msg);
            free(msg);
        }
        sleep(1);
    }
}

for (c = 0; c < server_num; c = c + 1)
    zmq_close(subscribers[c]);

zmq_ctx_destroy(context);
return 0;
8qgya5xd

8qgya5xd1#

作为存在的标记error-handling

Q:* 处理异常(while内部)的最正确方法是什么?*
最好的策略是***错误预防***,而不是*任何类型的“React性”(事后异常)处理。

总是假设事情可能会,并将成为破坏性的破坏,让他们失败便宜。失败的成本越低,系统就越好,也越快地恢复到它自己的预期行为。
也就是说,在现代低延迟分布式系统中,在实时系统中,异常是非常昂贵的,设计的代码执行流程的破坏性元素。

由于这些原因,以及为了保持最高性能的持续水平,ZeroMQ从此采用了一种非常不同的方法:

0)

在调用zmq_recv()的API函数之前,最好使用**zmq_poll()作为任何可读消息(已经发送并准备好接收)存在(或不存在)的最便宜的检测**,以便从Context()示例内部存储中将此类数据提取到应用程序级代码中。

1)

根据你的语言绑定( Package 器),最好享受.poll().send().recv()方法的非阻塞形式。本机API是最直接的,始终使用**retCode = zmq_recv( ..., ZMQ_NOBLOCK );**

2)
始终分析 * retCode-无论是在沉默或解释assert( retCode == 0 && zmq_errno() )或其他。
3)

最好的审查和微调ZeroMQ框架中可用的示例化工具的所有配置属性,并利用其所有隐藏的优势来最好地满足您的应用程序域的需求。许多原生API设置可能有助于减轻(如果不是主要避免)Context()引擎示例中的大量冲突要求,因此不要犹豫,了解可能设置的所有细节,并尽可能地使用它们为您的代码提供帮助。
如果不做以上所有这些,您的代码就不会 * 充分利用Zen-of-Zero

Q:* 在我现在的结构中(...),zmq_closezmq_ctx_destroy永远不会执行,但我希望它们能够执行,以防程序错误/异常(无论是哪个起源)。

设置一个明确标志是足够公平的:

bool    DoNotExitSoFar = True;

while ( DoNotExitSoFar ){
    // Do whatever you need

    // Record return-codes, always
       retCode = zmq_...(...);

    // Test/Set the explicit flag upon a context of retCode and zmq_errno()
       if ( retCode == EPROTONOTSUPPORTED ){
         // take all due measures needed
            ...
         // FINALLY: Set
            DoNotExitSoFar = False;
       }
}

// --------------------------------------------- GRACEFUL TERMINATION .close()
if ( ENOTSOCK == zmq_close(...) ) { ...; }
...
// --------------------------------------------- GRACEFUL TERMINATION .term()
retCode = zmq_ctx_term(...);

if ( EINTR  == retCode ){ ...; }
if ( EFAULT == retCode ){ ...; }
...

使用其他工具(如int atexit(void (*func)(void));)可以作为阿拉普调用zmq_close()zmq_ctx_term()的最后手段

3qpi33ja

3qpi33ja2#

你是对的,C没有try/catch的概念,但这不应该是一个问题。这只是意味着你需要在s_send()和s_recv()例程中处理异常(所以,例如,如果发生意外情况(如malloc()返回NULL),你必须处理它并继续处理或返回)。
我还建议您查看客户端的poll()或select()系统调用,而不是执行循环轮询。只为有数据等待读取的文件描述符提供服务要优雅得多。

fcipmucu

fcipmucu3#

C中检查错误的惯用方法是查看返回值,然后检查errno是否为负。

// ... Your previous code
int ret = zmq_ctx_destroy(context);
if(ret < 0) {
    // Process your error here
    printf("My error message is : %s\n", strerror(errno));
}

您可能需要添加#include <errno.h><string.h>,如果它不在您的程序中。您也可以阅读strerror文档。
现在回答问题的这一部分:
考虑到下面的代码片段,处理异常(在while中)的最正确方法是什么?使用我现在的结构(正如你在下面看到的),zmq_close和zmq_ctx_destroy永远不会执行,但我希望它们在程序错误/异常的情况下执行(无论是什么原因)。
所有zmq_*函数将返回一个错误并设置errno。检查每个功能,并在发生错误时检查break。在这种情况下,轮询非阻塞函数最好是在发生错误时将breakwhile循环中删除。
在Linux上,您还可以设置一个信号处理程序,并在引发信号时执行一个清理例程(例如,在控制台中的cxpls +C上捕获SIGINT以正确退出UNIX上的程序是非常常见的)。看到这个答案了吗

相关问题