文章目录
1. zmq_ctx_new(3)
Synopsis
void *zmq_ctx_new ();
Description
zmq_ctx_new()
函数创建了一个新的 ØMQ context。
thread safety
一个 ØMQ context 是线程安全的,可能在多个应用线程间共享,对于调用方而言不需要额外的锁。
Return value
如果成功,zmq_ctx_new()
函数应当返回一个指向新创建的context的不透明handle。否则它将返回NULL,并设置errno的值。
Errors
这个函数未定义任何错误。
2. zmq_socket(3)
Synopsis
void *zmq_socket (void *context, int type);
Description
zmq_socket()
函数使用指定的 context 将创建一个 ØMQ socket 并返回一个指向新创建socket的不透明 handle。类型参数指定套接字类型,它决定了在socket之上的通讯语义。
新创建的套接字的初始化状态是未绑定的,它与任何终端无关。为了建立一个消息流,一个socket必须首先使用zmq_connet()
与至少一个终端建立连接,或者必须使用zmq_bind()
创建一个终端来接收到来的连接。
较传统sockets的关键不同点
通常来说,传统的套接字提供面向连接的可靠字节流(SOCK_STREAM)或无连接的不可开数据报的同步接口。相较而言,ØMQ sockets 提供一个异步消息队列的抽象,依据使用的套接字类型有额外的队列语义。传统的套接字传输字节流或离散的数据报,而 ØMQ 套接字传输离散的消息。
ØMQ是异步的,这意味着物理连接的建立和离开,重新连接和有效交付的时间对用户来说是透明的,并且由ØMQ自己组织。此外,在peer无法接收消息的情况下,消息可能会被排队。
传统套接字仅允许严格意义上的one-to-one(two peers),many-to-one(many clinets, one server),或者在一些情况下的 one-to-many(multicast) 关系。除了ZMQ_PAIR,ØMQ 套接字可能使用zmq_connect()
连接多个终端,同时,使用zmq_bind()
接收多个终端到来的连接绑定到终端,因此允许many-to-many关系。
线程安全
ØMQ 有线程安全的套接字类型和非线程安全的套接字类型。应用不能在多线程中使用非线程安全的套接字类型,除非将套接字从一条线程迁移到另一条线程,并使用full fence
内存屏障。
下述是线程安全的套接字:* ZMQ_CLIENT * ZMQ_SERVER * ZMQ_DISH * ZMQ_RADIO * ZMQ_SCATTER * ZMQ_GATHER
套接字类型
下节介绍ØMQ定义的套接字类型,按照通用的消息模式分类–由相关的套接字类型构建。
发布订阅模式
发布-订阅模式用于one-to-many的数据分发,以扇形发散的方式,从单个的发布者分发到所有的订阅者处。
ZMQ_PUB
ZMQ_PUB类型的套接字被发布者用于发布数据。消息以扇形发散的方式发送到所有的连接peers。zmq_recv(3)
函数没有实现这个套接字的对应功能。
当 ZMQ_PUB 因为到达订阅者的高水位而进入静音状态。然后直到静音状态结束,任何将被发送到订阅者的消息都将被丢弃。
ZMQ_PUB特性 | |
---|---|
兼容的peer套接字 | ZMQ_SUB, ZMQ_XSUB |
方向 | 单向 |
发送/接收模式 | 仅发送 |
进入路由策略 | N/A |
外出路由策略 | Fan out |
静音状态行为 | 丢弃 |
ZMQ_SUB
ZMQ_SUB
套接字类型,被一个订阅者用于订阅发布者分发的数据。起初ZMQ_SUB
套接字没有订阅任何消息,使用 zmq_setsocketopt(3) 的 ZMQ_SUBSCRIBE
选项来指定要订阅哪些消息。对于ZMQ_SUB
类型的套接字来说,zmq_send()
函数没有实现。
ZMQ_SUB 的特性总结 | |
---|---|
兼容的peer套接字 | ZMQ_PUB, ZMQ_XPUB |
方向 | 单向 |
发送/接收模式 | 仅接收 |
进入路由策略 | Fair-queued |
外出路由策略 | N/A |
Example
使用 zmq_stream 创建一个简单的http server
void *ctx = zmq_ctx_new ();
assert (ctx);
/* Create ZMQ_STREAM socket */
void *socket = zmq_socket (ctx, ZMQ_STREAM);
assert (socket);
int rc = zmq_bind (socket, "tcp://*:8080");
assert (rc == 0);
/* Data structure to hold the ZMQ_STREAM routing id */
uint8_t routing_id [256];
size_t routing_id_size = 256;
/* Data structure to hold the ZMQ_STREAM received data */
uint8_t raw [256];
size_t raw_size = 256;
while (1) {
/* Get HTTP request; routing id frame and then request */
routing_id_size = zmq_recv (socket, routing_id, 256, 0);
assert (routing_id_size > 0);
do {
raw_size = zmq_recv (socket, raw, 256, 0);
assert (raw_size >= 0);
} while (raw_size == 256);
/* Prepares the response */
char http_response [] =
"HTTP/1.0 200 OK\r\n"
"Content-Type: text/plain\r\n"
"\r\n"
"Hello, World!";
/* Sends the routing id frame followed by the response */
zmq_send (socket, routing_id, routing_id_size, ZMQ_SNDMORE);
zmq_send (socket, http_response, strlen (http_response), 0);
/* Closes the connection by sending the routing id frame followed by a zero response */
zmq_send (socket, routing_id, routing_id_size, ZMQ_SNDMORE);
zmq_send (socket, 0, 0, 0);
}
zmq_close (socket);
zmq_ctx_destroy (ctx);
3. zmq_close(3)
Synopsis
int zmq_close (void *socket);
Description
zmq_close()
将会销毁socket参数引用的socket。任何从网络上接收到的还未被zmq_recv()接收的消息将会被丢弃。关于已经调用zmq_send()发送但是还没有被发送到网络上的消息的处理行为,取决于指定套接字的ZMQ_LINGER
套接字选项的值
对每个socket,zmq_close()
应当被显式的调用一次。如果它始终未被调用,zmq_ctx_term()
将会被永远阻塞。如果对相同的socket调用多次,或者参数socket
并没有指向一个socket,行为是未定义的。
ZMQ_LINGER 的默认设置不丢弃未发送的消息;这个行为可能会造成应用阻塞在`zmq_ctx_term()`的调用上。
更多细节查看`zmq_setsockopt(3)` 和 `zmq_ctx_term(3)`。
这个API将异步完成,所有当它返回后,不是所有的东西都被解分配了。
Return value
成功返回0,否则返回-1,并设置errno变量。
Errors
ENOTSOCK
- 参数socket为NULL
4. zmq_bind(3)
Synopsis
int zmq_bind (void *socket, const char *endpoint);
Description
zmq_bind()
函数绑定socket
到本地终端endpoint
,然后在终端上接受到来的连接。
终端是一个字符串,格式为transport://address
,transport指定底层使用的协议。address指定要绑定的指定传输方式的地址。
ØMQ提供下列传输方式:
tcp
- 使用TCP的单播传输,见
zmq_tcp(7)
ipc
- 本地进程间通信通讯方式,见
zmq_ipc(7)
inproc
- 本地进程内(线程间)通讯方式,见
zmq_inproc(7)
pgm, epgm
- 使用PGM的可靠多播传输,见
zmq_pgm(7)
vmci
- 虚拟机器通讯接口(VMCI),见
zmq_vmci(7)
除了 ZMQ_PAIR 外,每个套接字类型都支持one-to-many和many-to-one语义。明确的语义取决于套接字类型并被定义在zmq_socket(3)
中。
ipc,tcp和vmci传输方式支持 wildcard address。
对于 `zmq_bind()` 和 `zmq_connect()`,地址语法可能稍有不同,特别是tcp,pgm和epgm传输方式。
在`zmq_bind()`后,socket进入静音状态(`mute state`),除非或直到有至少一个进入或外出的连接建立,
此时socket进入准备状态(`ready state`)。在静音状态中,依据socket类型,socket阻塞或者丢弃消息。
相较而言,在libzmq::zmq_connect[3]之后,socket进入准备状态(ready state)
Return value
zmq_bind()
函数成功返回0。否则返回-1,并设置errno。
Errors
EINVAL
- 提供的终端无效
EPROTONOSUPPORT
- 请求运输方式不支持
ENOCOMPATPROTO
- 请求的传输协议不兼容套接字类型
EADDRINUSE
- 请求地址已经在使用中了
EADDRNOTAVAIL
- 请求地址不是本地的
ENODEV
- 请求地址指定不存在的接口
ETERM
- 与指定的socket相关的ØMQ context被终止了
ENOTSOCK
- 提供的套接字是非法的
EMTHREAD
- 没有可用的I/O线程来完成任务
Example
绑定发布者套接字到一个in-process和tcp的transport。
/* Create a ZMQ_PUB socket */
void *socket = zmq_socket (context, ZMQ_PUB);
assert (socket);
/* Bind it to a in-process transport with the address 'my_publisher' */
int rc = zmq_bind (socket, "inproc://my_publisher");
assert (rc == 0);
/* Bind it to a TCP transport on port 5555 of the 'eth0' interface */
rc = zmq_bind (socket, "tcp://eth0:5555");
assert (rc == 0);
5. zmq_connect(3)
Synopsis
int zmq_connect (void *socket, const char *endpoint);
Description
zmq_connect()
将套接字连接到一个终端上,然后在终端上接受到来的连接
。
终端字符串格式如下:transport://address。transport指定底层使用的协议,address指定要连接的传输指定的地址。
ØMQ提供下列传输方式:
tcp
- 使用TCP的单播传输,见
zmq_tcp(7)
ipc
- 本地进程间通信通讯方式,见
zmq_ipc(7)
inproc
- 本地进程内(线程间)通讯方式,见
zmq_inproc(7)
pgm, epgm
- 使用PGM的可靠多播传输,见
zmq_pgm(7)
vmci
- 虚拟机器通讯接口(VMCI),见
zmq_vmci(7)
除了 ZMQ_PAIR 外,每个套接字类型都支持one-to-many和many-to-one语义。明确的语义取决于套接字类型并被定义在zmq_socket(3)
中。
对大部分的传输和套接字类型而言,连接并非立即执行,而是依据 ØMQ 的需要执行。因此一次成功的
`zmq_connect` 函数的调用并不意味着连接已经或能够成功建立。因此,对大部分传输和套接字类型而言,
服务器执行绑定,客户端执行连接的顺序如何并不重要。`ZMQ_PAIR`套接字类型是个例外,因为它们不
会自动重连终端。
`zmq_connect()`之后,除了`ZMQ_ROUTER`类型外,其他套接字类型的套接字进入正常的就绪状态
(*ready* state)。仅当对peer的握手完成后,`ZMQ_ROUTE`套接字对一指定的peer进入它的正常就绪状态,
握手可能发生在任意时间。
对一些套接字类型,向相同的终端进行多重连接没有任何意义。对于这些套接字类型,任何尝试连接一个已经建立
连接的终端会被静默的忽略(比如,返回0)。这一行为应用于`ZMQ_DEALER`,`ZMQ_SUB`,`ZMQ_PUB`和`ZMQ_REQ`
Return value
zmq_bind()
函数成功返回0。否则返回-1,并设置errno。
Errors
EINVAL
- 提供的终端无效
EPROTONOSUPPORT
- 请求运输方式不支持
ENOCOMPATPROTO
- 请求的传输协议不兼容套接字类型
ETERM
- 与指定的socket相关的ØMQ context被终止了
ENOTSOCK
- 提供的套接字是非法的
EMTHREAD
- 没有可用的I/O线程来完成任务
Example
连接订阅套接字到一个in-process和一个tcp传输。
/* Create a ZMQ_SUB socket */
void *socket = zmq_socket (context, ZMQ_SUB);
assert (socket);
/* Connect it to an in-process transport with the address 'my_publisher' */
int rc = zmq_connect (socket, "inproc://my_publisher");
assert (rc == 0);
/* Connect it to the host server001, port 5555 using a TCP transport */
rc = zmq_connect (socket, "tcp://server001:5555");
assert (rc == 0);
6. zmq_socket_monitor(3)
Synopsis
int zmq_socket_monitor (void *socket, char *endpoint, int events);
Description
zmq_socket_monitor()
方法让一个应用线程追踪发生在ZeroMQ socket上的socket事件(比如连接)。对这个方法的每次调用都会创建一个ZMQ_PAIR
套接字并绑定到指定的inproc:// endpoint
。为了收集套接字事件,你必须创建你自己的ZMQ_PAIR
套接字,并向终端进行连接。
注意也有一个DRAFT函数zmq_socket_monitor_versioned(3)
,它允许订阅提供更多信息的事件。调用zma_socket_monitor()
等价于调用zmq_version_monitor_versioned(3)
,且其event_version参数设置为1,除了错误情况。
event参数是你希望监听的套接字事件的位掩码,见下述Supported below。为了监视所有事件,使用事件值ZMQ_EVENT_ALL
。NOTE:当新的事件被添加,catch-all值将开始返回它们。一个依赖于严格固定事件序列的应用必须不能使用ZMQ_EVENT_ALL
,为了保证未来版本的兼容性。
每个事件被作为两帧发送。第一帧包含事件序号(16位),和事件值(32位)–依据事件序号提供额外的数据。第二帧包含指定的受影响终端的字符串。
`_zmq_socket_moniter()_`仅支持面向连接的传输,那就是,TCP,IPC 和 TIPC
Supported events
ZMQ_EVENT_CONNECTED
Return value
如果成功,zmq_socket_monitor()
函数返回值为0或更大,否则返回-1并设置errno
Errors
Example
监视客户端和服务套接字
// Read one event off the monitor socket; return value and address
// by reference, if not null, and event number by value. Returns -1
// in case of error.
static int
get_monitor_event (void *monitor, int *value, char **address)
{
// First frame in message contains event number and value
zmq_msg_t msg;
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1)
return -1; // Interrupted, presumably
assert (zmq_msg_more (&msg));
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
uint16_t event = *(uint16_t *) (data);
if (value)
*value = *(uint32_t *) (data + 2);
// Second frame in message contains event address
zmq_msg_init (&msg);
if (zmq_msg_recv (&msg, monitor, 0) == -1)
return -1; // Interrupted, presumably
assert (!zmq_msg_more (&msg));
if (address) {
uint8_t *data = (uint8_t *) zmq_msg_data (&msg);
size_t size = zmq_msg_size (&msg);
*address = (char *) malloc (size + 1);
memcpy (*address, data, size);
(*address)[size] = 0;
}
return event;
}
int main (void)
{
void *ctx = zmq_ctx_new ();
assert (ctx);
// We'll monitor these two sockets
void *client = zmq_socket (ctx, ZMQ_DEALER);
assert (client);
void *server = zmq_socket (ctx, ZMQ_DEALER);
assert (server);
// Socket monitoring only works over inproc://
int rc = zmq_socket_monitor (client, "tcp://127.0.0.1:9999", 0);
assert (rc == -1);
assert (zmq_errno () == EPROTONOSUPPORT);
// Monitor all events on client and server sockets
rc = zmq_socket_monitor (client, "inproc://monitor-client", ZMQ_EVENT_ALL);
assert (rc == 0);
rc = zmq_socket_monitor (server, "inproc://monitor-server", ZMQ_EVENT_ALL);
assert (rc == 0);
// Create two sockets for collecting monitor events
void *client_mon = zmq_socket (ctx, ZMQ_PAIR);
assert (client_mon);
void *server_mon = zmq_socket (ctx, ZMQ_PAIR);
assert (server_mon);
// Connect these to the inproc endpoints so they'll get events
rc = zmq_connect (client_mon, "inproc://monitor-client");
assert (rc == 0);
rc = zmq_connect (server_mon, "inproc://monitor-server");
assert (rc == 0);
// Now do a basic ping test
rc = zmq_bind (server, "tcp://127.0.0.1:9998");
assert (rc == 0);
rc = zmq_connect (client, "tcp://127.0.0.1:9998");
assert (rc == 0);
bounce (client, server);
// Close client and server
close_zero_linger (client);
close_zero_linger (server);
// Now collect and check events from both sockets
int event = get_monitor_event (client_mon, NULL, NULL);
if (event == ZMQ_EVENT_CONNECT_DELAYED)
event = get_monitor_event (client_mon, NULL, NULL);
assert (event == ZMQ_EVENT_CONNECTED);
event = get_monitor_event (client_mon, NULL, NULL);
assert (event == ZMQ_EVENT_MONITOR_STOPPED);
// This is the flow of server events
event = get_monitor_event (server_mon, NULL, NULL);
assert (event == ZMQ_EVENT_LISTENING);
event = get_monitor_event (server_mon, NULL, NULL);
assert (event == ZMQ_EVENT_ACCEPTED);
event = get_monitor_event (server_mon, NULL, NULL);
assert (event == ZMQ_EVENT_CLOSED);
event = get_monitor_event (server_mon, NULL, NULL);
assert (event == ZMQ_EVENT_MONITOR_STOPPED);
// Close down the sockets
close_zero_linger (client_mon);
close_zero_linger (server_mon);
zmq_ctx_term (ctx);
return 0 ; }
7. zmq_setsockopt(3)
Synopsis
int zmq_setsockopt (void *socket, int option_name, const void *option_value, size_t option_len);
警告:所有的选项,除了 ZMQ_SUBSCRIBE, ZMQ_UNSUBSCRIBE, ZMQ_LINGER, ZMQ_ROUTER_HANDOVER, ZMQ_ROUTER_MANDATORY, ZMQ_PROBE_ROUTER, ZMQ_XPUB_VERBOSE, ZMQ_XPUB_VERBOSER, ZMQ_REQ_CORRELATE, ZMQ_REQ_RELAXED, ZMQ_SNDHWM 和 ZMQ_RCVHWM,仅仅对随后的绑定/连接的 socket 有效。
特别是,安全选项影响随后的 bind/connect 调用,能在任意时候改变以影响随后的绑定或连接。
Description
zmq_setsockopt
函数应当设置option_name
参数指定的选项,且使用option_value
参数指定的值,对socket
参数指定的 ØMQ socket 进行设置。option_len
参数是选项值的字节数。对于使用"character string"类型的选项,提供的字节数据不应当包含0字节,或者以单个0字节结尾(终止ASCII的NUL 字符)。
下述 socket 选项能使用 zmq_setsockopt() 函数设置:
ZMQ_HEARTBEAT_IVL: 设置发生 ZMQTP 心跳的间隔
ZMQ_HEATBEAT_IVL将对指定的 socket 设置发送 ZMTP 心跳的间隔。如果改选项被设置,且其值大于0,然后 PING ZMQ 命令将设置发送每一个 ZMQ_HEATBEAT_IVL 的毫秒数。
选项值类型 | int |
---|---|
选项值单位 | 毫秒 |
默认值 | 0 |
应用socket类型 | 所有,当使用面向连接的运输协议时 |
ZMQ_HEARTBEAT_TIMEOUT: 为 ZMTP 心跳设置超时
ZMQ_HERATBEAT_TIMEOUT
选项将设置等待时间,当在一个连接上发送一个PING_ZMTP 命令但是没有收到任何通信量,等待多久将其设置为超时。选项仅当ZMQ_HEARTBEAT_IVL也被设置且大于0时合法。当在发送PING命令后,没有收到任何通信量时,连接将超时,但是收到的通信量不一定非要是一个PONG命令-任何收到的通信量都能取消超时。
选项值类型 | int |
---|---|
选项值单位 | 毫秒 |
默认值 | 0 通常,ZMQ_HEARTBEART_IVL如果它被设置 |
应用socket类型 | 所有,当使用面向连接的传输时 |
ZMQ_HEARTBEAT_TTL: 设置 ZMTP 心跳的 TTL(Time To Live)的值
ZMQ_HEATBEAT_TTL选项将在 remote peer 为ZMTP心跳设置。如果选项大于0,remote side 将连接超时,如果它在TTL的时间段内没有接收更多的通信量。如果 ZMQ_HEATBEAT_IVL没有设置或者为0,这个选项没有任何影响。内部的,这个值将下舍入到最近的 decisecond,任何低于100的值将没有影响。
选项值类型 | int |
---|---|
选项值单位 | 毫秒 |
默认值 | 0 |
最大值 | 6553599,2^16-1 deciseconds |
应用socket类型 | 所有当使用面向连接的传输时 |