名称
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_new 和 zmq_poller_destroy 管理一个 poller 实例的生命周期。zmq_poller_new 创建并返回一个新的 poller 实例,而 zmq_poller_destroy 则销毁它。必须将一个指向有效 poller 的指针作为 zmq_poller_destroy 的 poller_p 参数传递。特别是,同一个 poller 实例不能多次调用 zmq_poller_destroy。如果成功执行,zmq_poller_destroy 会将传递的指针设置为 NULL。zmq_poller_destroy 会隐式地注销所有已注册的套接字和文件描述符。
zmq_poller_size 查询注册到 poller 的套接字或文件描述符的数量。poller 的初始大小为 0,成功的添加操作会使大小增加 1,成功的移除操作会使大小减少 1。大小不受指定事件的影响。
zmq_poller_add、zmq_poller_modify 和 zmq_poller_remove 管理注册到 poller 的 0MQ 套接字。
zmq_poller_add 将一个新的 socket 注册到给定的 poller。poller 和 socket 都必须指向有效的 0MQ 对象。events 参数指定客户端想要订阅的事件类型。可以合法地不指定任何事件(即 0),稍后通过 zmq_poller_modify 激活它们。此外,可以指定 user_data,poller 不使用它,但在调用 zmq_poller_wait 或 zmq_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_fd、zmq_poller_modify_fd 和 zmq_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' 的值为 0
,zmq_poller_wait_all 将立即返回。如果 'timeout' 的值为 -1
,zmq_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_add 和 zmq_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_add 和 zmq_poller_modify 函数没有影响,并且在 zmq_poller_event_t 结构体的 'events' 成员中永远不会设置。对于标准套接字,此标志通过 zmq_poller_wait_all 传递到底层的 poll() 系统调用,通常意味着由 'fd' 指定的套接字上存在某种错误情况。
- ZMQ_POLLPRI
-
对于 0MQ 套接字,此标志对 zmq_poller_add 和 zmq_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_add、zmq_poller_modify 和 zmq_poller_remove
- EFAULT
-
poller 没有指向有效的 poller。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。
- ENOTSOCK
-
socket 没有指向有效的套接字。请注意,传递无效指针(例如,指向已释放内存的指针)可能会导致未定义的行为(例如,访问冲突)。
对于 zmq_poller_add
- EMFILE
-
待办
对于 zmq_poller_add 或 zmq_poller_add_fd
- ENOMEM
-
无法分配必要的资源。
- EINVAL
-
socket 或 fd 已注册到 poller。
对于 zmq_poller_modify、zmq_poller_modify_fd、zmq_poller_remove 或 zmq_poller_remove_fd
- EINVAL
-
socket 或 fd 未注册到 poller。
对于 zmq_poller_add_fd、zmq_poller_modify_fd 和 zmq_poller_remove_fd
- EBADF
-
指定的 fd 是已弃用的文件描述符。
对于 zmq_poller_wait 和 zmq_poller_wait_all
- ENOMEM
-
无法分配必要的资源。
- ETERM
-
至少一个已注册对象是其关联的 0MQ 'context' 已终止的 'socket'。
- EFAULT
-
提供的 'events' 为 NULL,或者 'poller' 没有指向有效的 poller,或者没有已注册对象,或者所有事件订阅都已禁用且 'timeout' 为负。
- EINTR
-
在任何事件可用之前,操作被信号传递中断。
- EAGAIN
-
在达到超时之前,没有已注册事件发出信号。
对于 zmq_poller_fd
- EINVAL
-
poller 没有关联的文件描述符。
- EFAULT
-
提供的 'poller' 没有指向有效的 poller。
示例
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/。