消息的"释放"和"销毁"是两个不同的概念. zmq_msg_t其实是引用计数方式实现的共享对象类型, "释放"是指当前上下文放弃了对该消息的引用, 内部导致了实例的引用计数-1, 而"销毁"则是彻底把实例本身给free掉了. 当你"释放"一个消息的时候, 应当调用zmq_msg_close()接口. 如果消息实例在释放后引用计数归0, 那么这个消息实例会被ZMQ自动销毁掉.
要访问消息里包装的数据, 调用zmq_msg_data()接口, 要获取消息中数据的长度, 调用zmq_msg_size()
在你熟读并理解相关manpage中的内容之前, 不要去调用zmq_msg_move(), zmq_msg_copy(), zmq_msg_init_data()这三个接口
当你通过zmq_msg_send()调用将消息发送给socket后, 这个消息内部包装的数据会被清零, 也就是zmq_msg_size() == 0, 所以, 你不应该连续两次使用同一个zmq_msg_t *值调用zmq_msg_send(). 但需要注意的是, 这里的"清零", 并不代表消息被"释放", 也不代表消息被"销毁". 消息还是消息, 只是其中的数据被扔掉了.
如果你想把同一段二进制数据发送多次, 正确的做法是下面这样:
调用zmq_msg_init_size(), 创建第一个消息, 再通过memcpy或类似函数将二进制数据写入消息中
调用zmq_msg_init()创建第二个消息, 再调用zmq_msg_copy()从第一个消息将数据"复制"过来
重复上述步骤
依次调用zmq_msg_send()发送上面的多个消息
ZMQ还支持所谓的"多帧消息", 这种消息允许你把多段二进制数据一次性发送给对端. 这个特性在第三章我们再讲. (P.S.: 这是一个很重要的特性, 路由代理等高级套路就严重依赖这种多帧消息.). ZMQ中的消息有三层逻辑概念: 消息, 帧, 二进制数据. 用户自定义的二进制数据被包装成帧, 然后一个或多个帧组成一个消息. 消息是ZMQ拓扑网络中两个结点收发的单位, 但在ZMQ底层的传输协议中, 最小单位是帧.
换一个角度来讲, ZMQ使用其底层的传输协议, 比如tcp, 比如inproc, 比如ipc来传输数据, 当ZMQ调用这些传输协议传递数据的时候, 最小单元是帧. 帧的完整性由传输协议来保证, 即是ZMQ本身不关心这个帧会不会破损, 帧的完整传输应当由这些传输协议去保证. 而在使用ZMQ构建应用程序的程序员眼中, 最小的传输单位是消息, 一个消息里可能会有多个帧, 程序员不去关心消息从一端到另一端是否会出现丢帧, 消息的完整性与原子性应当由ZMQ库去保证.
前面我们讲过, ZMQ对其底层的传输协议是有侵入性的. 如果要了解ZMQ到底是如何在传输协议的基础上规定帧传输格式的, 可以去阅读这个规范.
在我们到达第三章之前, 我们所讨论的消息中都仅包含一个帧. 这就是为什么在这一小节的描述中, 我们几乎有引导性的让你觉得, zmq_msg_t类型, 就是"消息", 其实不是, 其实zmq_msg_t消息只是"帧".
一个消息可以由多个帧组成
每个帧都是一个zmq_msg_t对象
使用zmq_msg_send(), zmq_msg_recv(), 你可以一帧一帧的发送数据. 可以用多次调用这些接口的方式来发送一个完整的消息, 或者接收一个完整的消息: 在发送时传入ZMQ_SNDMORE参数, 或在接收时, 通过zmq_getsockopt()来获取ZMQ_RCVMORE选项的值. 更多关于如何使用低级API收发多帧消息的信息, 请参见相关接口的manpage
ZMQ也提供了便于收发多帧消息的高级API
关于消息或帧, 还有下面的一些特性:
ZMQ允许你发送数据长度为0的帧. 比如在有些场合, 这只是一个信号, 而没有任何语义上的数据需要被携带
ZMQ在发送多帧消息时, 保证消息的原子性与完整性. 如果丢失, 所有帧都不会到达对端, 如果成功, 那么必须所有帧都被正确送达, 帧在传输过程中不会出现破损.
在调用发送数据的癌后, 消息并不会被立即发出, 而是被放在发送缓冲区中. 这和zmq_send()是一致的.
你必须在完成消息接收后, 调用zmq_msg_close()接口来释放这个zmq_msg_t对象
最后再强调一下, 在你不理解zmq_msg_t的原理之前, 不要使用zmq_msg_init_data()接口, 这是一个0拷贝接口, 如果不熟悉zmq_msg_t结构的原理, 瞎jb用, 是会core dump的
ZMQ中的多路I/O复用在先前的所有例子程序中, 大多程序里干的都是这样的事情
等待socket上有数据
接收数据, 处理
重复上面的过程
如果你接触过linux中的select, pselect, epoll等多路IO复用接口, 你一定会好奇, 在使用zmq的时候, 如何实现类似的效果呢? 毕竟ZMQ不光把linux socket的细节给你封装了, 连文件描述符都给你屏蔽封装掉了, 显然你没法直接调用类似于select, pselect, epoll这种接口了.
答案是, ZMQ自己搞了一个类似的玩意, zmq_poll()了解一下.