名称

zmq_poller - 输入/输出多路复用

概要

void *zmq_poller_new (void);

int zmq_poller_destroy (void '**poller_p');

int zmq_poller_size (void '*poller');

int zmq_poller_add (void '*poller', void '*socket', void '*user_data', short 'events');

int zmq_poller_modify (void '*poller', void '*socket', short 'events');

int zmq_poller_remove (void '*poller', void '*socket');

int zmq_poller_add_fd (void '*poller', int 'fd', void '*user_data', short 'events');

int zmq_poller_modify_fd (void '*poller', int 'fd', short 'events');

int zmq_poller_remove_fd (void '*poller', int 'fd');

int zmq_poller_wait (void '*poller', zmq_poller_event_t '*event', long 'timeout');

int zmq_poller_wait_all (void '*poller', zmq_poller_event_t '*events', int 'n_events', long 'timeout');

int zmq_poller_fd (void '*poller', zmq_fd_t '*fd');

说明

zmq_poller* 函数提供了一种机制,供应用程序以水平触发的方式,对一组套接字上的输入/输出事件进行多路复用。

zmq_poller_newzmq_poller_destroy 管理一个 poller 实例的生命周期。zmq_poller_new 创建并返回一个新的 poller 实例,而 zmq_poller_destroy 则销毁它。必须将一个指向有效 poller 的指针作为 zmq_poller_destroypoller_p 参数传递。特别是,同一个 poller 实例不能多次调用 zmq_poller_destroy。如果成功执行,zmq_poller_destroy 会将传递的指针设置为 NULL。zmq_poller_destroy 会隐式地注销所有已注册的套接字和文件描述符。

zmq_poller_size 查询注册到 poller 的套接字或文件描述符的数量。poller 的初始大小为 0,成功的添加操作会使大小增加 1,成功的移除操作会使大小减少 1。大小不受指定事件的影响。

zmq_poller_addzmq_poller_modifyzmq_poller_remove 管理注册到 poller 的 0MQ 套接字。

zmq_poller_add 将一个新的 socket 注册到给定的 pollerpollersocket 都必须指向有效的 0MQ 对象。events 参数指定客户端想要订阅的事件类型。可以合法地不指定任何事件(即 0),稍后通过 zmq_poller_modify 激活它们。此外,可以指定 user_data,poller 不使用它,但在调用 zmq_poller_waitzmq_poller_wait_all 发出事件信号时会将其传回给调用者。user_data 可以是 NULL。如果不是 NULL,则必须是一个有效的指针。否则,行为是未定义的。每个套接字只能添加到单个 poller 实例一次(除非之前已经为该套接字调用了 zmq_poller_remove)。如果套接字本身是显式线程安全的(例如 Server, Client, …​),则可以将套接字添加到多个 poller 实例。如果套接字不是线程安全的,您可能会导致未定义的行为。

zmq_poller_modify 修改套接字的已订阅事件。可以合法地指定不订阅任何事件(即 0)来暂时禁用事件,然后稍后通过另一次调用 zmq_poller_modify 重新激活它们。

zmq_poller_remove 完全移除套接字注册。在通过 zmq_close 关闭套接字之前,必须调用 zmq_poller_remove

请注意,在调用 zmq_poller_destroy 之前,不必为任何套接字调用 zmq_poller_remove

另请注意,调用 zmq_poller_remove 与调用 zmq_poller_modify 并指定不订阅任何事件是不同的。zmq_poller_modify 不会释放与套接字注册相关的资源,并且要求 socket 保持有效。

zmq_poller_add_fdzmq_poller_modify_fdzmq_poller_remove_fd 与之前的函数类似,但管理注册到 poller 的常规文件描述符。在 Windows 上,这些函数只能用于 WinSock 套接字。

在下文中,通过 zmq_poller_add 添加的 0MQ 套接字和通过 zmq_poller_add_fd 添加的文件描述符统称为“已注册对象”。

zmq_poller_event_t 结构体定义如下:

typedef struct
{
    void *socket;
    zmq_fd_t fd;
    void *user_data;
    short events;
} zmq_poller_event_t;

对于每个已注册对象,zmq_poller_wait_all() 应检查已注册对象是否有当前已注册的事件。

如果没有发生任何已注册事件,zmq_poller_wait_all 将等待 'timeout' 毫秒,直到任何已注册对象上发生事件。如果 'timeout' 的值为 0zmq_poller_wait_all 将立即返回。如果 'timeout' 的值为 -1zmq_poller_wait_all 将无限期阻塞,直到任何已注册对象上发生一个事件。

zmq_poller_wait_all 的 'events' 参数必须是指向至少包含 'n_events' 个元素的数组的指针。如果 'events' 没有指向至少包含 'n_events' 个元素的数组,则行为是未定义的。

zmq_poller_wait_all 最多返回 'n_events' 个事件。如果发出的事件多于 'n_events' 个,则通过 'events' 只返回发出事件的未指定子集。

建议调用者确保 'n_events' 等于已注册对象的数量。否则,可能会导致活锁情况:如果在每次调用 zmq_poller_wait_all 时,具有活动事件的已注册对象多于 'n_events' 个,则可能总是返回相同的已注册对象子集,而调用者永远无法注意到其他对象上的事件。已注册对象的数量可以通过 zmq_poller_size 查询。

zmq_poller_wait_all 返回有效元素的数量。有效元素放置在 'events' 数组中的 '0' 到 'n_events - 1' 位置。有效元素的所有成员都由 zmq_poller_wait_all 设置为有效值。对于套接字事件,'socket' 非空,而 'fd' 是操作系统特定的无效套接字值(-1 或 INVALID_SOCKET)。对于文件描述符事件,'socket' 为 NULL,而 'fd' 是一个有效的文件描述符。因此,客户端在调用 zmq_poller_wait_all 之前无需初始化 events 数组的内容。至于 zmq_poller_wait_all 是否会写入 'events' 剩余元素的内容,这是未指定的。

zmq_poller_fd 查询与 zmq_poller 关联的文件描述符,并将其存储在 'fd' 指向的地址中。只有当至少注册了一个线程安全的套接字时,zmq_poller 才保证具有文件描述符。

请注意,关闭已注册到 poller 的套接字会导致未定义的行为。必须先注销该套接字。

事件类型

zmq_poller_addzmq_poller_modify 的 'events' 参数,以及 zmq_poller_event_t 结构体的 'events' 成员,是由以下事件标志组合通过按位或(OR)构成的位掩码:

ZMQ_POLLIN

对于 0MQ 套接字,可以从 'socket' 接收至少一条消息而不会阻塞。对于标准套接字,这等同于 poll() 系统调用的 'POLLIN' 标志,通常意味着可以从 'fd' 读取至少一个字节的数据而不会阻塞。

ZMQ_POLLOUT

对于 0MQ 套接字,可以向 'socket' 发送至少一条消息而不会阻塞。对于标准套接字,这等同于 poll() 系统调用的 'POLLOUT' 标志,通常意味着可以向 'fd' 写入至少一个字节的数据而不会阻塞。

ZMQ_POLLERR

对于 0MQ 套接字,此标志对 zmq_poller_addzmq_poller_modify 函数没有影响,并且在 zmq_poller_event_t 结构体的 'events' 成员中永远不会设置。对于标准套接字,此标志通过 zmq_poller_wait_all 传递到底层的 poll() 系统调用,通常意味着由 'fd' 指定的套接字上存在某种错误情况。

ZMQ_POLLPRI

对于 0MQ 套接字,此标志对 zmq_poller_addzmq_poller_modify 函数没有影响,并且在 zmq_poller_event_t 结构体的 'events' 成员中永远不会设置。对于标准套接字,这意味着有紧急数据可读。有关更多信息,请参阅 POLLPRI 标志。对于文件描述符,请参阅您的操作系统文档:例如,GPIO 中断通过 POLLPRI 事件发出信号。此标志在 Windows 上无效。

注意
zmq_poller* 函数可能使用 poll() 以外的操作系统接口实现或模拟,因此可能受到这些接口限制的影响,而这些限制并未在本文档中定义。

线程安全

与大多数其他 0MQ 对象一样,poller 不是线程安全的。所有操作必须从同一个线程调用。否则,行为是未定义的。

此外,如果您想将一个套接字添加到多个现有的 poller 实例中,套接字本身需要是线程安全的(例如 Server, Client, …​)。否则,行为是未定义的。

返回值

zmq_poller_new 返回指向 poller 的有效指针,失败时返回 NULL。

所有返回 int 的函数在失败时返回 -1。在这种情况下,可以使用 zmq_errno() 查询错误类型,如下所述。

zmq_poller_wait_all 返回在 events 数组中发出信号并返回的事件数量。它永远不会返回 0。

所有其他函数在成功执行时返回 0。

错误

对于 zmq_poller_new

ENOMEM

无法成功分配新的 poller。

对于 zmq_poller_destroy

EFAULT

poller_p 没有指向有效的 poller。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。

对于 zmq_poller_size

EFAULT

poller 没有指向有效的 poller。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。

对于 zmq_poller_addzmq_poller_modifyzmq_poller_remove

EFAULT

poller 没有指向有效的 poller。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。

ENOTSOCK

socket 没有指向有效的套接字。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。

对于 zmq_poller_add

EMFILE

待办

对于 zmq_poller_addzmq_poller_add_fd

ENOMEM

无法分配必要的资源。

EINVAL

socketfd 已注册到 poller。

对于 zmq_poller_modifyzmq_poller_modify_fdzmq_poller_removezmq_poller_remove_fd

EINVAL

socketfd 未注册到 poller。

对于 zmq_poller_add_fdzmq_poller_modify_fdzmq_poller_remove_fd

EBADF

指定的 fd 是已弃用的文件描述符。

对于 zmq_poller_waitzmq_poller_wait_all

ENOMEM

无法分配必要的资源。

ETERM

至少一个已注册对象是其关联的 0MQ 'context' 已终止的 'socket'。

EFAULT

提供的 'events' 为 NULL,或者 'poller' 没有指向有效的 poller,或者没有已注册对象,或者所有事件订阅都已禁用且 'timeout' 为负。

EINTR

在任何事件可用之前,操作被信号传递中断。

EAGAIN

在达到超时之前,没有已注册事件发出信号。

对于 zmq_poller_fd

EINVAL

poller 没有关联的文件描述符。

EFAULT

提供的 'poller' 没有指向有效的 poller。

示例

无限期轮询 0MQ 套接字和标准套接字上的输入事件。
void *poller = zmq_poller_new ();

/* First item refers to 0MQ socket 'socket' */
zmq_poller_add (poller, socket, NULL, ZMQ_POLLIN);
/* Second item refers to standard socket 'fd' */
zmq_poller_add_fd (poller, fd, NULL, ZMQ_POLLIN);

zmq_poller_event_t events [2];
/* Poll for events indefinitely */
int rc = zmq_poller_wait_all (poller, events, 2, -1);
assert (rc >= 0);
/* Returned events will be stored in 'events' */
for (int i = 0; i < 2; ++i) {
    if (events[i].socket == socket && events[i].events & ZMQ_POLLIN) {
        // ...
    } else if (events[i].fd == fd && events[i].events & ZMQ_POLLIN)) {
        // ...
    }
}
zmq_poller_destroy (&poller);

另请参见

作者

此页面由 0MQ 社区编写。要进行修改,请阅读 0MQ 贡献政策:https://zeromq.cn/how-to-contribute/