一、IO多路复用:zmq_poll()
int zmq_poll (zmq_pollitem_t *items, int nitems, long timeout);
- API参考文档:http://api.zeromq.org/4-3:zmq-poll
- 功能:实现I/O多路复用
- 参数:
- items:是一种zmq_pollitem_t结构的数组
- nitems:参数items数组的大小
- timeout:等待时间。可取的值如下:
- >0:等待指定的毫秒。指定的毫秒内items中有事件就绪则返回,否则等待到指定的毫秒超时返回
- 0:不等待,判断items后直接返回
- -1:永久阻塞等待。直到item上有事件返回
- 返回值:
- 成功:
- >0:返回items中就绪的事件的数量
- 0:timeout>0时表示zmq_poll()超时返回;timeout=0时表示没有事件就绪
- 失败:返回-1,并将errno设置为以下定义的值之一:
- ETERM:至少一个条目数组的成员是指一个套接字ØMQ上下文相关的终止
- EFAULT:items参数无效(NULL)
- EINTR:在发生任何事件之前,操作被信号中断了
- 相关描述:
- 对于items中的每个项目,zmq_poll()函数会轮询每个zmq_pollitem_t结构中的socket或fd,判断是否有事件返回
- 如果zmq_pollitem_t同时设置了socket和fd,则zmq_poll()只socket而忽略fd
- 对于items中的每个项目,zmq_poll()会首先清除revents成员,然后设置每个项目的revents成员对应的事件位标志来表示发生的事
zmq_pollitem_t结构
typedef struct
{
void *socket; //要轮询的ØMQ套接字
int fd; //或者,要轮询的本机文件句柄
short events; //要轮询的事件
short revents; //轮询后返回的事件
} zmq_pollitem_t;
- socket:要轮询的ØMQ套接字
- fd:要轮询的本机文件句柄
- events:要轮询的事件。可取的值如下,可以多个值进行|运算:
- ZMQ_POLLIN(可读):
- 对于ØMQ套接字(socket),可以从该套接字上接收到一条消息并且不会阻塞
- 对于标准套接字(fd),这相当于poll()系统调用的POLLIN标志,通常意味着可以从fd上至少读取一个字节的数据而不阻塞
- ZMQ_POLLOUT(可写):
- 对于ØMQ套接字(socket),可以从向该套接字发送一条消息并且不会阻塞
- 对于标准套接字(fd),这相当于poll()系统调用的POLLOUT标记,意味着至少有一个字节的数据可以在不阻塞的情况下写入fd
- ZMQ_POLLERR(错误发生):
- 对于标准套接字(fd),这个标志通过zmq_poll()传递给基础的poll()系统调用,通常意味着在fd指定的套接字上存在某种错误条件
- 对于ØMQ套接字(socket),这个标志没有任何影响,不会因为ØMQ套接字发生这个事件而导致zmq_poll()返回
- ZMQ_POLLPRI(紧急数据):
- 对于ØMQ套接字(socket),这个标志没有任何影响
- 对于标准套接字(fd),意味着有紧急数据到来。有关更多信息请参考POLLPRI标志。对于文件描述符,请参考您的用例:例如通过POLLPRI事件来发出GPIO中断信号。此标志在Windows上无效
- revents:轮询后返回的事件。可以设置的值与events相同
演示案例(伪代码)
zmq_pollitem_t items [2];
items[0].socket = socket;
items[0].events = ZMQ_POLLIN;
items[1].socket = NULL;
items[1].fd = fd;
items[1].events = ZMQ_POLLIN;
int rc = zmq_poll (items, 2, -1);
assert (rc >= 0);
二、演示案例
- 在前面的文章中我们演示过两个演示案例
- 下面的两个程序完成相同的功能:即作为发布订阅演示案例中的“天气状态更新的订阅者”,又作为流水线演示案例中的“并行任务的工人”。其中第一个演示案例不使用zmq_poll(),第二个演示案例使用zmq_poll()
不使用zmq_poll()实现
//msreader.c
#include <stdio.h>
#include <time.h>
#include <zmq.h>
static void s_sleep (int msecs);
int main()
{
// 1.初始化上下文
void *context = zmq_ctx_new();
// 2.连接到任务发生器
void *receiver = zmq_socket(context, ZMQ_PULL);
zmq_connect(receiver, "tcp://localhost:5557");
// 3.连接到天气服务器
void *subscriber = zmq_socket(context, ZMQ_SUB);
zmq_connect(subscriber, "tcp://localhost:5556");
zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "10001", 6);
// 4.处理来自两个套接字的消息, 先处理任务发生器, 再处理天气服务器
while(1)
{
// 5.先处理任务发生器的
int rc;
for(rc = 0; !rc; )
{
zmq_msg_t task;
zmq_msg_init(&task);
if((rc = zmq_msg_recv(&task, receiver, ZMQ_DONTWAIT)) != -1)
{
//...处理任务
}
zmq_msg_close(&task);
}
// 6.再处理天气服务器的
for(rc = 0; !rc; )
{
zmq_msg_t update;
zmq_msg_init(&update);
if((rc = zmq_msg_recv(&update, subscriber, ZMQ_DONTWAIT)) != -1)
{
//...处理天气状态更新
}
zmq_msg_close(&update);
}
s_sleep(1);
}
// 7.关闭套接字, 销毁上下文
zmq_close(receiver);
zmq_close(subscriber);
zmq_ctx_term(context);
return 0;
}
static void s_sleep (int msecs)
{
#if (defined (WIN32))
Sleep (msecs);
#else
struct timespec t;
t.tv_sec = msecs / 1000;
t.tv_nsec = (msecs % 1000) * 1000000;
nanosleep (&t, NULL);
#endif
}
使用zmq_poll()实现
//mspoller.c
#include <stdio.h>
#include <time.h>
#include <zmq.h>
static void s_sleep (int msecs);
int main()
{
// 1.初始化上下文
void *context = zmq_ctx_new();
// 2.连接到任务发生器
void *receiver = zmq_socket(context, ZMQ_PULL);
zmq_connect(receiver, "tcp://localhost:5557");
// 3.连接到天气服务器
void *subscriber = zmq_socket(context, ZMQ_SUB);
zmq_connect(subscriber, "tcp://localhost:5556");
zmq_setsockopt(subscriber, ZMQ_SUBSCRIBE, "10001", 6);
// 4.初始化轮询集
zmq_pollitem_t items[] = {
// 参数分别为: 轮询的套接字、轮询的本机文件句柄、要轮询的时间、轮询后返回的事件
{ receiver, 0, ZMQ_POLLIN, 0},
{ subscriber, 0, ZMQ_POLLIN, 0}
};
// 5.处理来自两个套接字的消息
while(1)
{
zmq_msg_t message;
// 6.轮询items, 最后一个参数为-1, 因此zmq_poll()会无限期阻塞等待事件
zmq_poll(items, 2, -1);
// 7.如果receiver套接字可读, 那么处理receiver套接字
if(items[0].revents & ZMQ_POLLIN)
{
zmq_msg_t(&message);
zmq_msg_recv(&message, receiver, 0);
//处理任务
zmq_msg_close(&message);
}
// 8.如果subscriber套接字可读, 那么处理subscriber套接字
if(items[1].revents & ZMQ_POLLIN)
{
zmq_msg_t(&message);
zmq_msg_recv(&message, subscriber, 0);
//处理天气更新状态
zmq_msg_close(&message);
}
}
// 7.关闭套接字, 销毁上下文
zmq_close(receiver);
zmq_close(subscriber);
zmq_ctx_term(context);
return 0;
}
static void s_sleep (int msecs)
{
#if (defined (WIN32))
Sleep (msecs);
#else
struct timespec t;
t.tv_sec = msecs / 1000;
t.tv_nsec = (msecs % 1000) * 1000000;
nanosleep (&t, NULL);
#endif
}