【GNU Radio】ZMQ模块学习
ZMQ 介绍
ZeroMQ (also known as ØMQ, 0MQ, or zmq) looks like an embeddable networking library but acts like a concurrency framework. It gives you sockets that carry atomic messages across various transports like in-process, inter-process, TCP, and multicast. You can connect sockets N-to-N with patterns like fan-out, pub-sub, task distribution, and request-reply. It’s fast enough to be the fabric for clustered products. Its asynchronous I/O model gives you scalable multicore applications, built as asynchronous message-processing tasks. It has a score of language APIs and runs on most operating systems.
——《ZeroMQ 官网》
ZeroMQ(也称为 ØMQ、0MQ 或 zmq)看起来像一个可嵌入的网络库,但其作用类似于并发框架。它为您提供了跨各种传输(如进程内、进程间、TCP 和多播)携带原子消息的套接字。您可以使用扇出、发布—订阅、任务分发、请求/响应等通信模式,N 对 N 地连接 socket。它的速度足以成为集群产品的结构。它的异步 I/O 模型可让你以异步消息处理任务的形式构建可扩展的多核应用程序。它拥有大量语言 API,可在大多数操作系统上运行。
ZeroMQ 提供了类似于 Socket 的一系列接口,它跟 Socket 的区别是:普通的 Socket 是端到端的(1对1的关系),而 ZMQ 却是可以 N 对 N 的关系。
前置知识
Socket
Socket(套接字)是计算机网络中的一种抽象概念,用于在不同设备或同一设备的不同进程之间进行通信。它提供了一种在应用程序和网络协议之间进行数据传输的接口。Socket 本身并不是协议,而是基于协议实现的一种通信机制。
套接字的概念起源于 Unix 系统,它允许程序之间进行双向通信,就像一个管道一样,数据可以从一端流入,从另一端流出。通过套接字,程序可以发送和接收数据,就像在电话线上进行通话一样。
套接字有多种,常见的有 BSD 套接字、Winsock 套接字等,按照套接字的功能和特性,可以将套接字分为以下几种:
- 流式套接字(SOCK_STREAM)
这种套接字类型使用 TCP 协议,是一种面向连接的套接字。它提供可靠的、双向的、有序的数据传输服务。数据在传输过程中不会丢失、重复或无序。流式套接字适用于需要稳定可靠数据传输的场景,如文件传输、网页浏览等。 - 数据报套接字(SOCK_DGRAM)
数据报套接字使用 UDP 协议,是一种无连接的套接字。它提供不可靠、无序的数据传输服务,数据在传输过程中可能会丢失、重复或无序。但它的传输速度较快,适用于对速度要求较高、对数据丢失不太敏感的场景,如视频直播、在线游戏等。 - 原始套接字(SOCK_RAW)
原始套接字允许应用程序直接访问较低级别的网络协议,如 IP 协议。它通常用于开发网络协议或工具等,具有较高的灵活性和复杂度,但使用门槛也较高。
套接字的用途较多,主要有以下几个原因:
跨平台支持
套接字是一种跨平台的通信机制,几乎所有的操作系统都支持套接字编程,这使得基于套接字开发的应用程序可以在不同的平台上运行。灵活性和通用性
套接字支持多种网络协议(如 TCP、UDP 等),可以根据不同的应用场景选择合适的协议。TCP 提供可靠的、面向连接的通信,适用于需要保证数据完整性和顺序的场景;UDP 则是无连接的、轻量级的通信方式,适用于对实时性要求较高的场景。丰富的应用场景
套接字可以用于实现各种网络应用,如 Web 服务器、邮件服务器、即时通讯工具、在线游戏等。在这些应用中,套接字提供了数据传输的基础。高效的通信方式
通过套接字,程序可以直接在底层网络协议上进行通信,从而减少中间层的开销,提高通信效率。
通信模型
PUB/SUB(发布/订阅)模型
PUB/SUB是一种异步通信模型,由发布者(Publisher)和订阅者(Subscriber)组成。
Publisher 将消息发送到特定主题(Topic),Subscriber 订阅感兴趣的主题以接收消息(即在此模型下,订阅者可以对发布者的消息进行过滤)。它们之间通过消息代理(Broker)进行消息传递,实现解耦。
这种模型适合一对多广播场景,例如实时事件分发、数据流处理等。
PUSH/PULL(推/拉)模型
PUSH/PULL也是一种异步通信模型,由发送者(PUSH)和接收者(PULL)组成。
PUSH 套接字将消息以负载均衡的方式发送给多个 PULL 套接字,每个消息只发送给一个 PULL 套接字。
适用于任务分发和负载均衡场景,如将任务分配给多个工作节点。
REQ/REP(请求/响应)模型
REQ/REP 是一种同步通信模型,由客户端(REQ)和服务器(REP)组成。客户端发送请求后等待服务器响应,服务器接收请求并返回响应。
适用于需要可靠传输和交互的应用,如远程过程调用(RPC)。
ZMQ 详解
基于通信模型分析
根据博文《[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解 》,GNU Radio 中的 ZMQ 块按照通信模型分为以下三种:
SINk | SOURCE | 特征 |
---|---|---|
PUB | SUB | 广播,可一对多 |
PUSH | PULL | 点播,点对点对等网络 |
REQ | REP | 点对点链路,一个请求一个回复,类似客户端服务器 |
其中,SOURCE 通常作为客户端,负责从 ZMQ 网络中获取数据并将其传递给 GNU Radio 的处理块。SINK 通常作为服务器,负责接收来自 GNU Radio 处理块的数据并通过 ZMQ 网络发送出去。
这种角色分配的原因在于数据流向和通信的发起方式:
- 数据流向
在 GNU Radio 的流图中,数据通常从 SOURCE 流向 SINK。SOURCE 从外部获取数据(如从网络、文件或硬件),而 SINK 将处理后的数据发送到外部。 - 通信的发起
在 ZMQ 中,SOURCE作为客户端主动连接到SINK(服务器),以确保数据可以正确地从 SOURCE 流向 SINK。这种设计使得通信的发起方(客户端)可以灵活地连接到服务器,而服务器只需要监听端口等待连接。
将 SINK 作为服务器的核心原因可以归结为:
被动接收数据
SINK 通常等待 SOURCE 发送数据,而不是主动请求数据。这符合服务器的特性,即被动等待客户端的连接和数据发送。简化网络配置
将 SINK 设置为服务器可以简化网络配置。服务器只需要绑定到一个端口并等待连接,而客户端(SOURCE)则需要知道服务器的地址和端口才能建立连接。这种设计使得 SINK 可以更方便地接收来自不同 SOURCE 的数据。符合 ZMQ 的通信模式
在 ZMQ 的 PUB/SUB 或 PUSH/PULL 模式中,数据的发送方(如 PUB 或 PUSH)通常需要先启动并绑定到一个端口,而接收方(SUB 或 PULL)则连接到该端口。在 GNU Radio 中,SINK 作为数据的发送方,自然承担服务器的角色。
基于数据格式分析
在 GNU Radio 中,ZMQ块按照块中的数据格式分为 Data Blocks 和 Message Blocks,区别如下:
Data Blocks
定义 | 用于传输原始流数据,没有格式化 |
数据处理方式 | 数据类型和采样率由发送端的流程图决定,接收端需要知道这些参数才能正确解读数据 |
适用场景 | 需要实时数据流处理、高效传输大量连续数据等场景 |
Message Blocks
定义 | 使用PMT(Polymorphic Types)对数据进行编码和解码 |
数据处理方式 | 数据是离散的消息单元,可以包含多个部分,每个部分都有明确的长度定义。消息是不透明的二进制数据,应用层需要自行进行序列化和反序列化 |
适用场景 | 需要传递结构化数据或进行复杂消息处理的场景,如远程过程调用(RPC)等 |
总结来说,Data Blocks适合传输连续的原始数据流,而Message Blocks则适合传输结构化的消息数据,两者的应用场景和数据处理方式各有侧重。
ZMQ 模块作用
对应于上述通信模型与数据格式,ZMQ 在 GNU Radio 中被分成 6 个 Source Block 和 6 个 Sink Block,每个 Source Block 都有相对应的 Sink Block。Source Block 用来传输进入 GNU Radio 的数据,而 Sink Block 则用来将数据传输出去。
以下为对应的12个 ZMQ 模块:
ZMQ PUB Message Sink & ZMQ SUB Message Source
ZMQ PUB Message Sink
作为发布者,从 GNU Radio 的流程图中接收消息,并通过 ZMQ 的 PUB 套接字发布出去。PUB 套接字可以有多个订阅者,这些订阅者可以是其他 GNU Radio 的 ZMQ SUB Message Source 块,也可以是非 GNU Radio 的 ZMQ 套接字。数据流向:GNU Radio 的流程图中 → P U B S o c k e t \xrightarrow{PUB~Socket} PUB Socket GNU Radio 的外面
ZMQ SUB Message Source
作为订阅者,从 ZMQ 的 SUB 套接字接收消息,并将这些消息提供给 GNU Radio 的流程图进行处理。它会订阅由 PUB 套接字发布的内容。数据流向:GNU Radio 的流程图中 ← S U B S o c k e t \xleftarrow{SUB~Socket} SUB Socket GNU Radio 的外面
ZMQ PUB Sink & ZMQ SUB Source
ZMQ PUB Sink
与 ZMQ PUB Message Sink 类似,但传输的是原始流数据而不是消息。该组件通过将数据发送到 ZMQ 的 PUB 套接字,供多个订阅者接收。数据流向:GNU Radio 的流程图中 → P U B S o c k e t \xrightarrow{PUB~Socket} PUB Socket GNU Radio 的外面
ZMQ SUB Source
作为订阅者,从 ZMQ 的 SUB 套接字接收原始流数据,并将其作为数据源提供给 GNU Radio 的流程图。数据流向:GNU Radio 的流程图中 ← S U B S o c k e t \xleftarrow{SUB~Socket} SUB Socket GNU Radio 的外面
ZMQ REQ Message Sink & ZMQ REP Message Source
ZMQ REQ Message Sink
作为客户端,该组件向 ZMQ 的 REQ 套接字发送请求消息并等待来自 REP 套接字的回复,将从 GNU Radio 的流程图中接收到的消息传输出去。数据流向:GNU Radio 的流程图中 → R E Q S o c k e t \xrightarrow{REQ~Socket} REQ Socket GNU Radio 的外面
ZMQ REP Message Source
作为服务器端,该组件从 ZMQ 的 REP 套接字接收请求消息,并在处理后发送回复消息。该组件会一直等待请求的到来。数据流向:GNU Radio 的流程图中 ← R E P S o c k e t \xleftarrow{REP~Socket} REP Socket GNU Radio 的外面
ZMQ REQ Sink & ZMQ REP Source
ZMQ REQ Sink
与 ZMQ REQ Message Sink 类似,但该组件处理的是原始流数据。该组件通过 REQ 套接字发送数据请求,并接收来自 REP 套接字的回复数据。数据流向:GNU Radio 的流程图中 → R E Q S o c k e t \xrightarrow{REQ~Socket} REQ Socket GNU Radio 的外面
ZMQ REP Source
作为服务器端,该组件从 REP 套接字接收数据请求,并发送回复数据。该组件用于点对点链路,一个请求对应一个回复。数据流向:GNU Radio 的流程图中 ← R E P S o c k e t \xleftarrow{REP~Socket} REP Socket GNU Radio 的外面
ZMQ PUSH Message Sink & ZMQ PULL Message Source
ZMQ PUSH Message Sink
该组件将消息推送到 ZMQ 的 PUSH 套接字,通常用于将消息发送到一个或多个 PULL 套接字,适用于点对点的对等网络。数据流向:GNU Radio 的流程图中 → P U S H S o c k e t \xrightarrow{PUSH~Socket} PUSH Socket GNU Radio 的外面
ZMQ PULL Message Source
作为接收端,该组件从 ZMQ 的 PULL 套接字接收消息,并将这些消息提供给 GNU Radio 的流程图进行处理。数据流向:GNU Radio 的流程图中 ← P U L L S o c k e t \xleftarrow{PULL~Socket} PULL Socket GNU Radio 的外面
ZMQ PUSH Sink & ZMQ PULL Source
ZMQ PUSH Sink
与 ZMQ PUSH Message Sink 类似,但传输的是原始流数据。该组件将数据推送到 PUSH 套接字,供 PULL 套接字接收。数据流向:GNU Radio 的流程图中 → P U S H S o c k e t \xrightarrow{PUSH~Socket} PUSH Socket GNU Radio 的外面
ZMQ PULL Source
作为接收端,从 PULL 套接字接收原始流数据,并作为数据源提供给 GNU Radio 的流程图。数据流向:GNU Radio 的流程图中 ← P U L L S o c k e t \xleftarrow{PULL~Socket} PULL Socket GNU Radio 的外面
关于 ZMQ 模块的具体使用可以参照《[SDR] GNU Radio 系列教程(十四) —— GNU Radio 低阶到高阶用法的分水岭 ZMQ 的使用详解 》、《ZeroMQ-Python》和《ZeroMQ-GNURadio》。