我使用 ZeroMQ 创建了一个发布者-订阅者通信方案,我注意到我的服务器和客户端程序存在一个小问题。 我知道 C 中没有 try catch(根据我的简短研究),但是接下来的两个
while(1)
没有异常捕获对我来说似乎很危险。
考虑到以下代码片段,处理异常(在 while 内)的最正确方法是什么?根据我现在的结构(如下所示),
zmq_close
和zmq_ctx_destroy
永远不会执行,但我希望它们能够执行,以防出现程序错误/异常(无论来源如何)。
注意: 在此架构中,我有一个客户端监听多个发布者,因此
for
在 Client 代码中循环。
服务器
(...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;
作为标签错误处理存在:
Q:处理异常(在 while 内)的最正确方法是什么?
最好的策略是错误预防,而不是任何类型的“反应式”(事后异常)处理。
永远假设事情可能并且将会造成严重破坏,并让它们以低廉的代价失败。失败的成本越低,系统就可以更好、更快地恢复到它自己的预期行为。
这就是说,在现代低延迟分布式系统中,在实时系统中,异常是极其昂贵的,是设计的代码执行流的破坏性元素。
0)
最好使用
zmq_poll()
作为任何可读消息(已发送并准备好接收)的存在(或不存在)的最便宜的检测,在调用之前(如果有的话) zmq_recv()
的 API 函数,用于从 Context()
实例内部存储内部将此类数据获取到应用程序级代码的手中。
1)
根据您的语言绑定(包装器),最好享受 .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_close
永远不会执行,但我希望它们能够执行,以防出现程序错误/异常(无论来源如何) .zmq_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));
可能是 ALAP 调用 zmq_close()
或 zmq_ctx_term()
的最后手段
你是对的,C 没有 try/catch 的概念,但这不应该是一个问题。它只是意味着您需要处理 s_send() 和 s_recv() 例程中的异常(因此,例如,如果发生意外情况(例如 malloc() 返回 NULL),您必须处理它并继续处理或返回)。
我还建议您查看客户端的 poll() 或 select() 系统调用,而不是进行循环轮询。只为有数据等待读取的文件描述符提供服务要优雅得多。
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
。在这种情况下,当发生错误时,对非阻塞函数的轮询最好是 break
退出 while
循环。
在 Linux 上,您还可以设置信号处理程序并在发出信号时执行清理例程(例如,在 UNIX 上通过控制台中的 ctrl+C 捕获 SIGINT 来正确退出程序是很常见的)。看这个答案