ZeroMQ:06---基础篇之(zmq_socket、zmq_close、zmq_bind、zmq_unbind、zmq_connect、disconnect、zmq_socket_monitor)

一、ØMQ套接字的相关说明

ZeroMQ的连接与旧式的TCP连接有些不同

  • 主要的明显不同是:
    • 它们跨任意的传输协议(inproc、ipc、tcp、pgm或epgm)。参阅zmq_inproc()、zmq_ipc()、zmq_tcp()、zmq_pgm()、zmq_epgm()
    • 一个套接字可能会有很多输入和输出连接
    • 不存在zmq_listen()、zmq_accept()方法,当一个套接字被绑定到一个端点的时候,它自定地开始监听、接受连接
    • 网络连接本身是在后台发生的,而如果网络连接断开(例如,对等节点消失后又回来),ZeroMQ会自动地重新连接
    • 应用程序不能与这些连接直接交流,它们是被封装在套接字之下的

各个节点可以任意地启动和停止

  • 在传统的网络中,如果我们在服务器未启动的情况下使用客户端去连接服务器,那么客户端将返回错误,但是ZeroMQ允许我们任意地启动和停止各部件

提前写入数据

  • 假设服务端未启动,客户端先调用zmq_connect()连接指定的服务端,之后客户端就可以向套接字写入数据了,客户端会将消息写入队列,当后面服务端再调用zmq_bind()启动的时候就会收到客户端之前发送过来的数据
  • 如果客户端在服务端启动之前,写入的消息过多导致消息被丢弃,那么服务端就不会收到丢失的数据

演示案例如下

  • ②在右侧开启服务端接收客户端的数据,可以看到左侧客户端不断的发送数据,右侧服务端也收到数据

一个服务端节点可以绑定多个端点

  • 一个服务器节点可以绑定到多个端点(即协议和地址的组合),只需要单个套接字就能够做到。这意味着它能接受不同的传输协议:
void *socket = zmq_socket(xxx);

zmq_bind(socket, "tcp://*:5555");
zmq_bind(socket, "tcp://*:9999");
zmq_bind(socket, "inproc://somename");
  • 除了ipc传输协议,其他传输协议不能将地址绑定到一个已经被启动的端点上
  • 但是ipc传输协议允许一个进程绑定到已被其他进行使用的端点,这意味着允许一个进程在崩溃后恢复

通信协议

  • ØMQ提供了一组单播传输(inproc、ipc和tcp)和多播传输(epgm、pgm)
  • 多播是一种先进的技术,我们在后面文章讲解
  • tcp:
    • tcp是最常使用的情况,这是一个断开连接的TCP传输。它是可伸缩的、可移植的,并且在大多数情况下足够快
    • 我们之所以称之为“断开连接”,是因为ØMQ的TCP传输在你连接到端点前,不需要端点存在。客户端和服务器可以在任何时候连接和绑定,并且它对应用程序保持透明
  • ipc:
    • 进程间的ipc传输也是断开连接的。有一个限制:它不能在Windows上工作
    • 按照惯例,zmq_bind()时我们使用“.ipc”扩展名为端点命名,以避免与其他文件名产生冲突。在Unix系统中,如果你使用ipc端点,创建ipc文件需要适当权限,否则,它们可能无法在不同用户ID运行的进程之间共享。你还必须确保所有的进程都可以访问该文件,例如,通过在同一个工作目录下运行
  • inproc:
    • 在线程间的传输中,inproc是一个连接的信令传送,它比tcp或ipc快得多
    • 与tcp和ipc相比,这种传输有特定的限制,但服务器必须在任何客户端发出连接之前,发出一个绑定请求。ØMQ的未来版本可能会解决这个问题,但目前这定义了你如何使用inproc套接字:我们创建和绑定一个套接字,并启动子线程,从而创建并连接其他套接字

二、套接字的创建(zmq_socket)

void *zmq_socket(void *context, int type);
  • API参考文档:http://api.zeromq.org/master:zmq-socket
  • 功能:创建套接字
  • 参数:
    • context:关联的上下文句柄
    • type:指定创建的套接字的类型。见下面详细介绍
  • 返回值:
    • 成功:返回套接字句柄,在ZeroMQ中,套接字是无类型的指针
    • 失败:返回NULL,并将errno设置为如下值之一:
      • EINVAL:请求的套接字类型无效
      • EFAULT:所提供的上下文无效。
      • EMFILE:ZeroMQ指定的套接字最大数量已满
      • ETERM:指定的上下文已终止
  • 与常规套接字的关键区别:
    • 一般来说,传统的套接字提供了到面向连接的可靠字节流(SOCK_STREAM)或无连接的不可靠数据报(SOCK_DGRAM)的同步接口。相比之下,ØMQ socket提供了异步消息队列的抽象,其确切的排队语义取决于使用的套接字类型
    • 在常规套接字传输字节流或离散数据报流的地方,ØMQ套接字传输离散消息
    • ØMQ套接字是异步的,意味着物理连接的建立、关闭、重新连接和有效教父的时间堆用户是透明的,并由ØMQ自己组织。此外,在对等端无法接收消息时,消息可能会排队
    • 传统的套接字只允许严格的一对一(两个对等点)、多对一(许多客户端、一台服务器),或者在某些情况下只允许一对多(多播)关系。除了ZMQ_PAIR,ØMQ套接字可被连接到多个端点使用zmq_connect(),同时接受传入的连接来自多个端点绑定套接字使用zmq_bind(),从而允许多对多的关系
  • 线程安全:

    • ØMQ具有线程安全的套接字类型,也有线程不安全的套接字类型。应用程序不得使用多线程的非线程安全套接字,除非在将套接字从一个线程迁移到另一个具有“完全隔离”内存屏障的线程之后
    • 下面是线程安全套接字:
      • ZMQ_CLIENT
      • ZMQ_SERVER
      • ZMQ_DISH
      • ZMQ_RADIO
      • ZMQ_SCATTER
      • ZMQ_GATHER

客户端-服务器模式(Client-server)

  • 客户机-服务器模式用于允许一个ZMQ_SERVER服务器与一个或多个ZMQ_CLIENT客户机通信。客户端总是启动对话,之后任何一方都可以向另一方异步发送消息
  • 客户机-服务器模式由http://rfc.zeromq.org/spec:41正式定义
  • 该模式仍在草案阶段

ZMQ_CLIENT

  • ZMQ_CLIENT套接字与ZMQ_SERVER套接字通信。任何一个对等点都可以连接,但是通常推荐的模型是绑定ZMQ_SERVER并连接ZMQ_CLIENT
  • 如果ZMQ_CLIENT套接字已经建立了连接,zmq_send()将接受消息,将它们排成队列,并在网络允许的情况下尽可能快地发送它们。传出缓冲区限制由套接字的高水位标志定义。如果传出缓冲区已满,或者没有连接的对等点,zmq_send()将默认阻塞。ZMQ_CLIENT套接字不会删除消息
  • 当ZMQ_CLIENT套接字连接到多个ZMQ_SERVER套接字时,发送出去的消息将在连接的对等端之间循环分发。同样,ZMQ_CLIENT套接字公平地从每个连接的对等端接收消息。这种用法仅适用于无状态协议
  • ZMQ_CLIENT套接字是线程安全的,可以从多个线程同时使用。注意,来自ZMQ_SERVER套接字的响应将发送到调用zmq_msg_recv()的第一个客户机线程。如果需要获得对原始线程的响应,每个线程使用一个ZMQ_CLIENT套接字
  • ZMQ_CLIENT套接字是线程安全的。它们在发送时不接受ZMQ_SNDMORE选项,而在接收时不接受ZMQ_RCVMORE。这就限制了他们只能使用单个部件的数据。其目的是扩展API以允许分散/收集多部分数据
                                                   ZMQ_CLIENT特性摘要 
兼容的对等套接字 ZMQ_SERVER
方向 双向的
发送/接收模式 无限制
外发路由策略 扇出(Fan out)
入网路由策略 公平排队
静音状态下的操作 阻塞

ZMQ_SERVER

  • ZMQ_SERVER套接字与一组ZMQ_CLIENT套接字通信。ZMQ_SERVER套接字只能应答传入消息:ZMQ_CLIENT对等端必须始终发起对话
  • 每个接收到的消息都有一个routing_id,它是一个32位无符号整数。应用程序可以使用zmq_msg_routing_id(3)来获取它。要向给定的ZMQ_CLIENT对等点发送消息,应用程序必须使用zmq_msg_set_routing_id(3)在消息上设置对等点的routing_id
  • 如果没有指定routing_id,或者没有引用已连接的客户端对等点,则发送调用将在EHOSTUNREACH中失败。如果客户端对等端的传出缓冲区已满,发送调用将阻塞,除非在发送中使用ZMQ_DONT_WAIT,在这种情况下,它将通过EAGAIN失败。ZMQ_SERVER套接字在任何情况下都不应该丢失消息
  • ZMQ_SERVER套接字是线程安全的。它们在发送时不接受ZMQ_SNDMORE选项,而在接收时不接受ZMQ_RCVMORE。这就限制了他们只能使用单个部件的数据。其目的是扩展API以允许分散/收集多部分数据
                                                   ZMQ_SERVER特性摘要 
兼容的对等套接字 ZMQ_CLIENT
方向 双向的
发送/接收模式 无限制
外发路由策略 扇出(Fan out)
入网路由策略 公平排队
静音状态下的操作 返回EAGAIN

广播盘模式(Radio-dish)

  • 广播盘模式用于以扇出方式将数据从单个发布者一对多分发到多个订户。
  • Radio-dish正在使用组(相对于Pub-sub主题),Dish套接字可以加入一个组,Radio套接字发送的每个消息都属于一个组。
  • 组是限制为16个字符长度(包括null)的以null终止的字符串。目的是将长度增加到40个字符(包括null)。组的编码应为UTF8。
  • 使用完全匹配(vs PubSub的前缀匹配)来匹配组
  • 广播碟仍处于草案阶段

ZMQ_RADIO

  • 发布者使用ZMQ_RADIO类型的套接字来分发数据。每个消息都属于一个组,使用zmq_msg_set_group()指定一个组。邮件将分发给组中的所有成员。所述zmq_recv()函数不是此套接字类型实现。
  • 当ZMQ_RADIO套接字由于已达到订户的最高水位而进入静音状态时,将发送给有问题的订户的任何消息都将被丢弃,直到静音状态结束为止。对于该套接字类型,zmq_send()函数将永远不会阻塞。
  • ZMQ_RADIO套接字是线程安全的。他们在发送时不接受ZMQ_SNDMORE选项。这将它们限制为单个零件数据
                                                   ZMQ_RADIO特性摘要 
兼容的对等套接字 ZMQ_DISH
方向 单向
发送/接收模式 仅发送
入网路由策略 不适用(N/A)

外发路由策略

扇出(Fan out)
静音状态下的操作 下降(Drop)

ZMQ_DISH

  • 用户使用ZMQ_DISH类型的套接字来订阅由无线电分发的组。最初,ZMQ_DISH套接字未订阅任何组,请使用zmq_join()加入一个组。要获取该组,消息属于zmq_msg_group()。该zmq_send()函数没有此套接字类型实现。
  • ZMQ_DISH套接字是线程安全的。他们不接受ZMQ_RCVMORE。这将它们限制为单个零件数据。
                                                   ZMQ_DISH特性摘要 
兼容的对等套接字 ZMQ_RADIO
方向 单向
发送/接收模式 仅接收
入网路由策略 公平排队
外发路由策略 不适用(N/A)

发布-订阅模式(Publish-subscribe)

ZMQ_PUB

  • 发布者使用类型为ZMQ_PUB的套接字来分发数据。发送的消息以扇出方式分发给所有连接的对等方
  • 不能在ZMQ_PUB类型的套接字上执行zmq_msg_recv()等接收数据的函数
  • 当ZMQ_PUB套接字由于已达到订阅者的高水位标记而进入静音状态时,将发送给有问题的订阅者的任何消息都将被丢弃,直到静音状态结束为止。关于“高水位标记”请参阅:
  • 对于该套接字类型,zmq_msg_send()函数将永远不会阻塞
                                                   ZMQ_PUB特性摘要 
兼容的对等套接字 ZMQ_SUB、ZMQ_XSUB
方向 单向
发送/接收模式 仅发送
入网路由策略 不适用(N/A)

外发路由策略

扇出(Fan out)
静音状态下的操作 下降(Drop)

ZMQ_SUB

  • 订阅者使用ZMQ_SUB类型的套接字来订阅发布者分发的数据
  • ZMQ_SUB套接字创建完成之后,ZMQ_SUB套接字未订阅任何消息,请使用zmq_setsockopt()的ZMQ_SUBSCRIBE选项指定要订阅的消息
  • 不能在ZMQ_PUB类型的套接字上执行zmq_msg_recv()等接收数据的函数
                                                   ZMQ_SUB特性摘要 
兼容的对等套接字 ZMQ_PUB、ZMQ_XPUB
方向 单向
发送/接收模式 仅接收
入网路由策略 公平排队

外发路由策略

不适用(N/A)

ZMQ_XPUB

  • 用法与ZMQ_PUB大部分相同
  • 但是有一点与ZMQ_PUB不同:ZMQ_XPUB(自己)的订阅方可以向自己发送一个订阅信息来进行订阅。订阅消息是字节1(用于订阅)或字节0(用于取消订阅),后跟订阅主体。也接收不带子/取消订阅前缀的消息,但对订阅状态没有影响
                                                   ZMQ_XPUB特性摘要 
兼容的对等套接字 ZMQ_SUB、ZMQ_XSUB
方向 单向
发送/接收模式 发送消息,接收订阅
入网路由策略 不适用(N/A)

外发路由策略

扇出(Fan out)
静音状态下的操作 下降(Drop)

ZMQ_XSUB

  • 用法与ZMQ_SUB大部分相同
  • 但是有一点与ZMQ_SUB不同:自己可以向发布者发送一条订阅信息来进行订阅。订阅消息是字节1(用于订阅)或字节0(用于取消订阅),后跟订阅主体。也接收不带子/取消订阅前缀的消息,但对订阅状态没有影响
                                                   ZMQ_XSUB特性摘要 
兼容的对等套接字 ZMQ_PUB、ZMQ_XPUB
方向 单向
发送/接收模式 接收消息,发送订阅
入网路由策略 公平排队

外发路由策略

不适用(N/A)
静音状态下的操作 下降(Drop)

流水线模式(Pipeline)

ZMQ_PUSH

  • 流水线节点使用类型为ZMQ_PUSH的套接字将消息发送到下游流水线节点。消息循环到所有连接的下游节点。该zmq_recv()函数没有此套接字类型实现。
  • 当ZMQ_PUSH套接字由于已达到所有下游节点的高水位线而进入静音状态时,或者如果根本没有下游节点,则套接字上的任何zmq_send()操作都应阻塞,直到静音状态结束或处于至少一个下游节点可用于发送;消息不会被丢弃。
                                                   ZMQ_PUSH特性摘要 
兼容的对等套接字 ZMQ_PULL
方向 单向
发送/接收模式 仅发送
入网路由策略 不适用(N/A)

外发路由策略

轮询
静音状态下的操作 阻塞

ZMQ_PULL

  • 管道节点使用ZMQ_PULL类型的套接字从上游管道节点接收消息。消息从所有连接的上游节点中公平排队。该zmq_send()函数没有此套接字类型实现
                                                   ZMQ_PULL特性摘要 
兼容的对等套接字 ZMQ_PUSH
方向 单向
发送/接收模式 仅接收
入网路由策略 公平排队

外发路由策略

不适用(N/A)
静音状态下的操作 阻塞

独占对模式(Exclusive pair)

  • 互斥对模式用于将一个对等点精确地连接到另一个对等点。此模式用于跨inproc传输的线程间通信
  • 互斥对模式由http://rfc.zeromq.org/spec:31正式定义

ZMQ_PAIR

  • ZMQ_PAIR类型的套接字只能一次连接到单个对等方。对通过ZMQ_PAIR套接字发送的消息不执行消息路由或筛选
  • 当ZMQ_PAIR套接字由于已达到连接对等方的高水位线而进入静音状态时,或者如果没有连接任何对等方,则套接字上的任何zmq_send()操作都应阻塞,直到对等方可用于发送;消息不会被丢弃
  • 尽管ZMQ_PAIR套接字可用于zmq_inproc()以外的其他传输方式,但是它们无法自动重新连接,同时新的传入连接将被终止,而任何先前的连接(包括处于关闭状态的连接)都会使它们不适合TCP在大多数情况下
  • ZMQ_PAIR套接字设计用于通过zmq_inproc()传输进行线程间通信,并且不实现自动重新连接等功能
                                                   ZMQ_PAIR特性摘要 
兼容的对等套接字 ZMQ_PAOR
方向 双向
发送/接收模式 无限制
入网路由策略 不适用(N/A)

外发路由策略

不适用(N/A)
静音状态下的操作 阻塞

本机模式(Native)

  • 本机模式用于与TCP对等方通信,并允许在任一方向上进行异步请求和答复。

ZMQ_STREAM

  • 一个套接字类型的ZMQ_STREAM用于发送和接收TCP数据从non-ØMQ对等,当使用TCP:/ /运输。ZMQ_STREAM套接字可以充当客户端和/或服务器,异步发送和/或接收TCP数据。
  • 当接收TCP数据时,在将消息传递给应用程序之前,ZMQ_STREAM套接字应在消息部分之前包含消息的始发对等方的路由ID。接收到的消息从所有连接的同级之间公平排队。
  • 发送TCP数据时,ZMQ_STREAM套接字应删除消息的第一部分,并使用它来确定消息应路由到的对等方的路由ID,并且不可路由的消息将导致EHOSTUNREACH或EAGAIN错误。
  • 要打开与服务器的连接,请使用zmq_connect调用,然后使用带有ZMQ_ROUTING_ID选项的zmq_getsockopt调用获取套接字路由ID。
  • 要关闭特定连接,请发送路由ID帧,后跟零长度消息(请参见示例部分)。
  • 建立连接后,应用程序将收到零长度的消息。同样,当对等方断开连接(或连接断开)时,应用程序将接收零长度的消息。
  • 建立连接后,应用程序将收到零长度的消息。同样,当对等方断开连接(或连接断开)时,应用程序将接收零长度的消息。
  • 您必须先发送一个路由ID帧,然后发送一个数据帧。ZMQ_SNDMORE标志是路由ID帧所必需的,但在数据帧上将被忽略。
                                                   ZMQ_STREAM特性摘要 
兼容的对等套接字
方向 双向
发送/接收模式 无限制
入网路由策略 公平排队

外发路由策略

看上面介绍
静音状态下的操作 EAGAIN

请求-回复模式(Request-reply)

ZMQ_REQ

  • 客户端使用ZMQ_REQ类型的套接字向服务发送请求并从服务接收答复
  • 此套接字类型仅允许zmq_send(request)和后续zmq_recv(reply)调用交替序列。发送的每个请求都在所有服务中轮流轮询,并且收到的每个答复都与最后发出的请求匹配
  • 如果没有可用的服务,则套接字上的任何发送操作都应阻塞,直到至少有一项服务可用为止。REQ套接字不会丢弃消息
                                                   ZMQ_REQ特性摘要 
兼容的对等套接字 ZMQ_REP、ZMQ_ROUTER
方向 双向
发送/接收模式 发送、接收、发送、接收......
入网路由策略 最后一位(Last peer)

外发路由策略

轮询
静音状态下的操作 阻塞

ZMQ_REP

  • 服务使用ZMQ_REP类型的套接字来接收来自客户端的请求并向客户端发送回复
  • 此套接字类型仅允许zmq_recv(request)和后续zmq_send(reply)调用的交替序列。接收到的每个请求都从所有客户端中公平排队,并且发送的每个回复都路由到发出最后一个请求的客户端
  • 如果原始请求者不再存在,则答复将被静默丢弃
                                                   ZMQ_REP特性摘要 
兼容的对等套接字 ZMQ_REQ、ZMQ_DEALER
方向 双向
发送/接收模式 接收、发送、接收、发送......
入网路由策略 公平排队

外发路由策略

最后一位(Last peer)

ZMQ_DEALER

  • ZMQ_DEALER类型的套接字是用于扩展请求/应答套接字的高级模式。发送的每条消息都是在所有连接的对等方之间进行轮询,并且收到的每条消息都是从所有连接的对等方进行公平排队的。
  • 当ZMQ_DEALER套接字由于已达到所有对等点的最高水位而进入静音状态时,或者如果根本没有任何对等点,则套接字上的任何zmq_send()操作都应阻塞,直到静音状态结束或至少一个对等方变得可以发送;消息不会被丢弃。
  • 将ZMQ_DEALER套接字连接到ZMQ_REP套接字时,发送的每个消息都必须包含一个空的消息部分,定界符以及一个或多个主体部分
                                                   ZMQ_DEALER特性摘要 
兼容的对等套接字 ZMQ_ROUTER、ZMQ_REP、ZMQ_DEALER
方向 双向
发送/接收模式 无限制
入网路由策略 公平排队

外发路由策略

轮询
静音状态下的操作 阻塞

ZMQ_ROUTER

  • ZMQ_ROUTER类型的套接字是用于扩展请求/答复套接字的高级套接字类型。当收到消息时,ZMQ_ROUTER套接字在将消息传递给应用程序之前,应在消息部分之前包含消息的始发对等方的路由ID。接收到的消息从所有连接的同级之间公平排队。发送消息时,ZMQ_ROUTER套接字应删除消息的第一部分,并使用它来确定消息应路由到的对等方的_routing id _。如果该对等点不再存在或从未存在,则该消息将被静默丢弃。但是,如果ZMQ_ROUTER_MANDATORY套接字选项设置为1,这两种情况下套接字都将失败并显示EHOSTUNREACH。
  • 当ZMQ_ROUTER套接字由于达到所有同位体的高水位线而进入静音状态时,发送到该套接字的任何消息都将被丢弃,直到静音状态结束为止。同样,任何路由到已达到单个高水位标记的对等方的消息也将被丢弃。如果ZMQ_ROUTER_MANDATORY设置为1,则在两种情况下套接字都应阻塞或返回EAGAIN。
  • 当ZMQ_ROUTER套接字的ZMQ_ROUTER_MANDATORY标志设置为1时,套接字应在接收到来自一个或多个对等方的消息后生成ZMQ_POLLIN事件。同样,当至少一个消息可以发送给一个或多个对等方时,套接字将生成ZMQ_POLLOUT事件。
  • 当ZMQ_REQ套接字连接到ZMQ_ROUTER套接字时,除了始发对等方的路由ID外,每个收到的消息都应包含一个空的定界符消息部分。因此,由应用程序看到的每个接收到的消息的整个结构变为:一个或多个路由ID部分,定界符部分,一个或多个主体部分。将回复发送到ZMQ_REQ套接字时,应用程序必须包括定界符部分
                                                   ZMQ_ROUTER特性摘要 
兼容的对等套接字 ZMQ_DEALER、ZMQ_REQ、ZMQ_ROUTER
方向 双向
发送/接收模式 无限制
入网路由策略 公平排队

外发路由策略

看上面介绍
静音状态下的操作 掉落(见上面介绍)
  • 演示案例:使用zmq_stream创建一个简单的http服务器
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);

三、套接字的关闭(zmq_close)

int zmq_close(void *socket);
  • API参考文档:http://api.zeromq.org/master:zmq-close

  • 功能:关闭套接字
  • 参数:
    • socket:要关闭的套接字
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并将errno设置为如下值之一:
      • ENOTSOCK:提供的套接字为NULL

备注

  • zmq_close()函数将销毁套接字参数引用的套接字。使用zmq_recv()从网络物理上接收到但应用程序还没有接收到的任何未完成的消息都将被丢弃。丢弃使用zmq_send()的应用程序发送但尚未物理传输到网络的消息的行为取决于指定套接字的ZMQ_LINGER套接字选项的值。
  • 对于每个套接字,必须准确地调用zmq_close()一次。如果没有调用它,zmq_ctx_term()将永远阻塞。如果为同一套接字多次调用该套接字,或者套接字不指向某个套接字,则该行为是未定义的
  • ZMQ_LINGER的默认设置不会丢弃未发送的消息;这种行为可能导致应用程序在调用zmq_ctx_term()时阻塞。详细信息请参考zmq_setsockopt()和zmq_ctx_term()
  • 这个API将异步完成,所以在它返回后并不是所有的东西都将被释放

四、套接字的绑定、监听、接收(zmq_bind)

int zmq_bind(void *socket, const char *endpoint);
  • API参考文档:http://api.zeromq.org/master:zmq-bind
  • 功能:对指定的套接字进行绑定、监听、接收连接。传统的套接字需要bind()、然后listen()、然后再进行accept()。zmq_bind()函数可以一次性完成这三个步骤,其在内部进行了listen和accept
  • 参数:
    • socket:操作的套接字
    • endpoint:指定绑定、监听、接收的地址。由“transport://followed”格式组成,关于transport的类型见下
      • tcp:使用TCP的单播传输,请参阅zmq_tcp
      • ipc:本地进程间通信传输,参见zmq_ipc
      • inproc:本地进程内(线程间)通信传输,参见zmq_inproc
      • pgm,epgm:使用PGM的可靠多播传输,参见zmq_pgm
      • vmci:虚拟机通信接口(VMCI),参见zmq_vmci
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并将errno设置为如下值之一:
      • EINVAL:提供的端点无效
      • EPROTONOSUPPORT:不支持请求的传输协议
      • ENOCOMPATPROTO:请求的传输协议与套接字类型不兼容
      • EADDRINUSE:所请求的地址已被使用
      • EADDRNOTAVAIL:请求的地址不是本地地址
      • ENODEV:请求的地址指定了不存在的接口
      • ETERM:与指定套接字关联的ØMQ 上下文已终止
      • ENOTSOCK:提供的套接字无效
      • EMTHREAD:没有可用的I / O线程来完成任务

备注

  • 除了ZMQ_PAIR以外,每种ØMQ套接字类型都支持一对多和多对一的语义。精确的语义取决于套接字类型,并在zmq_socket中定义
  • ipc、tcp和vmci传输接受通配符地址,详细信息请参阅zmq_ipc()、zmq_tcp()和zmq_vmci()
  • zmq_bind()和zmq_connect()的地址语法可能不同,特别是对于tcp、pgm和epgm传输
  • 在zmq_bind()之后,除非进入或直到至少建立一个传入或传出连接,否则套接字进入静音状态,此时套接字进入就绪状态。在静音状态下,套接字根据zmq_socket()中定义的套接字类型阻止或丢弃消息。相反,在libzmq:zmq_connect之后,套接字进入就绪状态

演示案例

  • 将发布者套接字绑定到进程内和tcp传输
/* 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);

五、zmq_unbind

int zmq_unbind (void *socket, const char *endpoint);
  • API参考文档:http://api.zeromq.org/4-3:zmq-unbind
  • 功能:停止接受套接字上的连接
  • 相关描述:
    • zmq_unbind()函数将socket所指的套接字从endpoint所指的端点上解除绑定
    • 从套接字中解除通配符地址绑定:当在zmq_bind()中使用通配符*端点(zmq_tcp()、zmq_ipc()和zmq_vmci())时,调用者应该使用从ZMQ_LAST_ENDPOINT套接字选项获得的真实端点来从套接字解除该端点的绑定
  • 参数:
    • socket:操作的套接字
    • endpoint:要连接的地址。由“transport://followed”格式组成,关于transport的类型见下:
      • tcp:使用TCP的单播传输,请参阅zmq_tcp
      • ipc:本地进程间通信传输,参见zmq_ipc
      • inproc:本地进程内(线程间)通信传输,参见zmq_inproc
      • pgm,epgm:使用PGM的可靠多播传输,参见zmq_pgm
      • vmci:虚拟机通信接口(VMCI),参见zmq_vmci
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并将errno设置为如下值之一:
      • EINVAL:提供的端点无效
      • ETERM:与指定套接字关联的ØMQ 上下文已终止
      • ENOTSOCK:提供的套接字无效
      • ENOENT:提供的端点先前未绑定

六、zmq_connect

int zmq_connect(void *socket, const char *endpoint);
  • API参考文档:http://api.zeromq.org/master:zmq-connect
  • 功能:向指定的地址发起连接
  • 参数:
    • socket:操作的套接字
    • endpoint:要连接的地址。由“transport://followed”格式组成,关于transport的类型见下:
      • tcp:使用TCP的单播传输,请参阅zmq_tcp
      • ipc:本地进程间通信传输,参见zmq_ipc
      • inproc:本地进程内(线程间)通信传输,参见zmq_inproc
      • pgm,epgm:使用PGM的可靠多播传输,参见zmq_pgm
      • vmci:虚拟机通信接口(VMCI),参见zmq_vmci
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并将errno设置为如下值之一:
      • EINVAL:提供的端点无效
      • EPROTONOSUPPORT:不支持请求的传输协议
      • ENOCOMPATPROTO:请求的传输协议与套接字类型不兼容
      • ETERM:与指定套接字关联的ØMQ 上下文已终止
      • ENOTSOCK:提供的套接字无效
      • EMTHREAD:没有可用的I/O线程来完成任务

演示案例

  • 将订阅服务器套接字连接到进程内和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);

七、断开套接字(zmq_disconnect)

int zmq_disconnect (void *socket, const char *endpoint);
  • API参考文档:http://api.zeromq.org/master:zmq-disconnect
  • 功能:断开套接字,将socket指定的套接字与其连接的endpoint端点断开
  • 参数:
    • socket:操作的套接字
    • endpoint:要断开的端点。格式可以参阅zmq_connect()函数
  • 相关描述:
    • zmq_recv():任何物理上从网络接收但尚未被zmq_recv()应用程序接收的未完成消息都将被丢弃
    • zmq_send():丢弃应用程序使用zmq_send()发送但尚未物理传输到网络的消息的行为取决于指定套接字的zmq_LINGER套接字选项的值。ZMQ_LINGER的默认设置不会丢弃未发送的消息;此行为可能会导致应用程序在调用ZMQ_ctx_term()时阻塞。有关详细信息,请参阅zmq_setsockopt()和zmq_ctx_term()
  • 返回值:
    • 成功:返回0
    • 失败:返回-1,并将errno设置为如下的值之一:
      • EINVAL:提供的端点无效
      • ETERM:与指定套接字关联的ØMQ上下文已终止
      • ENOTSOCK:提供的套接字无效
      • ENOENT:提供的端点未连接

演示案例

  • 将订户套接字连接到进程内和TCP传输
/* Create a ZMQ_SUB socket */
void *socket = zmq_socket (context, ZMQ_SUB);
assert (socket);

/* Connect it to the host server001, port 5555 using a TCP transport */
rc = zmq_connect (socket, "tcp://server001:5555");
assert (rc == 0);

/* Disconnect from the previously connected endpoint */
rc = zmq_disconnect (socket, "tcp://server001:5555");
assert (rc == 0);

八、监听套接字事件(zmq_socket_monitor)

int zmq_socket_monitor (void *socket, char *endpoint, int events);
  • API参考文档:http://api.zeromq.org/master:zmq-socket-monitor
  • 功能:监听套接字事件
  • 参数:
    • socket:监听的套接字
    • endpoint:要绑定的ZMQ_PAIR套接字地址,自己创建并传入
    • events:监听的事件
  • 返回值:
    • 成功:返回0或更大的值
    • 失败:返回-1,并将errno设置为如下的值之一:
      • ETERM:与指定套接字关联的ØMQ 上下文已终止
      • EPROTONOSUPPORT:不支持请求的传输协议。需要使用监视器套接字才能使用inproc://传输
      • EINVAL:提供的端点无效
  • 描述:
    • zmq_socket_monitor()方法让应用程序线程跟踪ZeroMQ套接字上的套接字事件(比如连接)。对该方法的每次调用都会创建一个ZMQ_PAIR套接字,并将其绑定到指定的inproc://端点。要收集套接字事件,您必须创建自己的ZMQ_PAIR套接字,并将其连接到端点
    • 注意,还有一个草案函数zmq_socket_monitor_versioned(),它允许订阅提供更多信息的事件。调用zmq_socket_monitor相当于调用将event_version参数设置为1的zmq_socket_monitor_versioned,但错误情况除外
    • events参数是要监视的套接字事件的位掩码,请参阅下面的支持事件。要监视所有事件,请使用事件值ZMQ_EVENT_ALL。注意:随着新事件的添加,all - catch-all值将开始返回它们。依赖严格且固定的事件序列的应用程序不能使用ZMQ_EVENT_ALL,以保证与未来版本的兼容性。
    • 每个事件以两帧的形式发送。第一帧包含事件编号(16位)和事件值(32位),根据事件编号提供附加数据。第二个框架包含指定受影响端点的字符串。
    • _zmq_socket_monitor()_方法只支持面向连接的传输,即TCP、IPC和TIPC

  • 参数events支持的值如下:

ZMQ_EVENT_CONNECTED

  • 套接字已成功连接到远程对等方。事件值是基础网络套接字的文件描述符(FD)。警告:不能保证您的代码收到该事件时FD仍然有效

ZMQ_EVENT_CONNECT_DELAYED

  • 套接字上的连接请求正在等待处理。未指定事件值。

ZMQ_EVENT_CONNECT_RETRIED

  • 连接请求失败,现在正在重试。事件值是重新连接间隔(以毫秒为单位)。请注意,每次重试都会重新计算重新连接间隔。

ZMQ_EVENT_LISTENING

  • 套接字已成功绑定到网络接口。事件值是基础网络套接字的FD。警告:不能保证您的代码收到该事件时FD仍然有效。

ZMQ_EVENT_BIND_FAILED

  • 套接字无法绑定到给定的接口。事件值是系统绑定调用生成的errno。

ZMQ_EVENT_ACCEPTED

  • 套接字已接受来自远程对等方的连接。事件值是基础网络套接字的FD。警告:不能保证您的代码收到该事件时FD仍然有效。

ZMQ_EVENT_ACCEPT_FAILED

  • 套接字已拒绝来自远程对等方的连接。事件值是由accept调用生成的errno

ZMQ_EVENT_CLOSED

  • 套接字已关闭。事件值是(已关闭)网络套接字的FD。

ZMQ_EVENT_CLOSE_FAILED

  • 套接字关闭失败。事件值是系统调用返回的errno。请注意,此事件仅在IPC传输上发生。

ZMQ_EVENT_DISCONNECTED

  • 套接字意外断开连接。事件值是基础网络套接字的FD。警告:此套接字将被关闭。

ZMQ_EVENT_MONITOR_STOPPED

  • 在此套接字上的监视已结束。

ZMQ_EVENT_HANDSHAKE_FAILED_NO_DETAIL

  • 握手期间发生未指定的错误。事件值为errno。

ZMQ_EVENT_HANDSHAKE_SUCCEEDED

  • ZMTP安全机制握手成功。未指定事件值。

ZMQ_EVENT_HANDSHAKE_FAILED_PROTOCOL

  • ZMTP安全机制握手由于ZMTP机制对等点之间或机制服务器与ZAP处理程序之间的某种机制协议错误而失败。这表明任一对等体响应中存在配置或实现错误。ZAP处理程序。事件值为ZMQ_PROTOCOL_ERROR_ *值之一:
    • ZMQ_PROTOCOL_ERROR_ZMTP_UNSPECIFIED ZMQ_PROTOCOL_ERROR_ZMTP_UNEXPECTED_COMMAND
    • ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_SEQUENCE ZMQ_PROTOCOL_ERROR_ZMTP_KEY_EXCHANGE
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_UNSPECIFIED
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_MESSAGE
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_HELLO
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_INITIATE
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_ERROR
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_READY
    • ZMQ_PROTOCOL_ERROR_ZMTP_MALFORMED_COMMAND_WELCOME
    • ZMQ_PROTOCOL_ERROR_ZMTP_INVALID_METADATA ZMQ_PROTOCOL_ERROR_ZMTP_CRYPTOGRAPHIC
    • ZMQ_PROTOCOL_ERROR_ZMTP_MECHANISM_MISMATCH ZMQ_PROTOCOL_ERROR_ZAP_UNSPECIFIED
    • ZMQ_PROTOCOL_ERROR_ZAP_MALFORMED_REPLY ZMQ_PROTOCOL_ERROR_ZAP_BAD_REQUEST_ID
    • ZMQ_PROTOCOL_ERROR_ZAP_BAD_VERSION ZMQ_PROTOCOL_ERROR_ZAP_INVALID_STATUS_CODE
    • ZMQ_PROTOCOL_ERROR_ZAP_INVALID_METADATA

ZMQ_EVENT_HANDSHAKE_FAILED_AUTH

  • ZMTP安全机制握手由于身份验证失败而失败。事件值是ZAP处理程序返回的状态代码(即300、400或500)。

演示案例

  • 监视客户端和服务器套接字
// 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 ; }

猜你喜欢

转载自blog.csdn.net/qq_41453285/article/details/106784970