15/ZMTP

15/ZMTP

ZeroMQ 消息传输协议

ZeroMQ 消息传输协议 (ZMTP) 是一种传输层协议,用于通过连接的传输层(例如 TCP)在两个对等方之间交换消息。本文档描述 ZMTP/2.0。

许可协议

版权所有 (c) 2009-2012 iMatix Corporation

本规范是自由软件;您可以根据自由软件基金会发布的 GNU 通用公共许可协议的条款重新分发和/或修改它;可以选择许可协议的第 3 版本或任何更高版本。

本规范的分发是希望它会有用,但没有任何担保;甚至不包括适销性或特定用途适用性的默示担保。有关详细信息,请参阅 GNU 通用公共许可协议。

您应该已经随本程序收到一份 GNU 通用公共许可协议的副本;如果没有,请参阅 https://gnu.ac.cn/licenses

变更流程

本规范是一个自由开放标准(请参阅“自由开放标准的定义”),并受数字标准组织的共识导向规范系统 (COSS) 管辖(请参阅“共识导向规范系统”)。

语言

本文档中的关键词“MUST”、“MUST NOT”、“REQUIRED”、“SHALL”、“SHALL NOT”、“SHOULD”、“SHOULD NOT”、“RECOMMENDED”、“MAY”和“OPTIONAL”应按 RFC 2119 中的描述进行解释(请参阅“用于 RFC 中表示需求级别的关键词”)。

目标

ZeroMQ 消息传输协议 (ZMTP) 是一种传输层协议,用于通过连接的传输层(例如 TCP)在两个对等方之间交换消息。本文档描述 ZMTP/2.0。

理论上,ZMTP 应该定义实现之间完全互操作的行为。然而,必要语义的部分内容仅在 libzmq 的代码和参考手册中定义。我们希望随着时间的推移,这些语义能够被适当提取、抽象、文档化,并由独立代码证明。

实现

形式化语法

以下 ABNF 语法定义了 ZMTP/2.0 协议

zmtp        = *connection

connection  = greeting *message

greeting    = signature revision socket-type identity
signature   = %xFF 8%x00 %x7F
revision    = %x01

socket-type = PAIR | PUB | SUB | REQ | REP | DEALER | ROUTER | PULL | PUSH
PAIR        = %X00
PUB         = %X01
SUB         = %X02
REQ         = %X03
REP         = %X04
DEALER      = %X05
ROUTER      = %X06
PULL        = %X07
PUSH        = %X08

identity    = final-short body
final-short = %x00 OCTET
body        = *OCTET

message     = *more-frame final-frame
final-frame = final body
final       = final-short | final-long
final-long  = %x02 8OCTET
more-frame  = more body
more        = more-short | more-long
more-short  = %x01 OCTET
more-long   = %x03 8OCTET

帧格式

ZMTP 将 TCP 流划分为“帧”。为了便于结构化,一个消息可以包含多个帧。一个帧由一个标志字段、一个长度字段和一个长度为若干个八位字节(octet)的帧体组成。长度不包括标志字段本身,也不包括长度字段本身,因此空帧的长度为零。

标志字段包含一个八位字节,其中包含各种控制标志。位 0 是最低有效位(最右边的位)。

  • 位 0 (MORE):后面还有更多帧。值为 0 表示后面没有更多帧。值为 1 表示后面还有更多帧。对于由单个帧组成的消息,MORE 位 MUST 是 0。

  • 位 1 (LONG):长消息。值为 0 表示消息长度编码为一个八位字节。值为 1 表示消息长度编码为一个 64 位无符号整数,采用网络字节序。

  • 位 2-7:保留。位 2-7 保留供将来使用,且 MUST 为零。

以下图表显示了长度为 0 到 255 个八位字节的最终帧布局

            +-----------------+
 Octet 0    | 0 0 0 0 0 0 0 0 |
            +-----------------+
 Octet 1    | Length          |
            +-----------------+- ... -----------------+
 Octets 2+  | Body                      Length octets |
            +------------------- ... -----------------+

以下图表显示了最终 LONG 帧的布局

            +-----------------+
 Octet 0    | 0 0 0 0 0 0 1 0 |
            +-----------------+
 Octets 1-8 | Length                       8 octets   |
            +------------------ ... ------------------+
 Octets 9+  | Body                      Length octets |
            +------------------ ... ------------------+

Socket 兼容性

实现 SHOULD 强制要求传入连接具有有效的 socket 类型,这取决于接收连接的 socket 的 socket 类型

  • PAIR 接受来自 PAIR 的连接。
  • PUB 接受来自 SUB 的连接。
  • SUB 接受来自 PUB 的连接。
  • REQ 接受来自 REP 或 ROUTER 的连接。
  • REP 接受来自 REQ 或 DEALER 的连接。
  • DEALER 接受来自 REP、DEALER 或 ROUTER 的连接。
  • ROUTER 接受来自 REQ、DEALER 或 ROUTER 的连接。
  • PULL 接受来自 PUSH 的连接。
  • PUSH 接受来自 PULL 的连接。

其他任何 socket 组合 SHOULD 通过静默断开与对方对等方的连接来处理,并可能记录错误以供调试。

发布-订阅

XPUB 和 XSUB socket 在协议层面实现为 PUB 和 SUB socket。也就是说,XPUB 和 XSUB 仅是 API 构造。SUB socket 发送订阅消息,其格式为一个字节 %x01 后跟订阅体;发送取消订阅消息,其格式为一个字节 %x00 后跟订阅体。

向后互操作性

ZMTP/2.0 使用一个八位字节来指示协议修订号。ZMTP/2.0 被视为协议的修订版本 1。ZMTP/1.0(请参阅“13/ZMTP - ZeroMQ 消息传输协议”)没有任何版本信息。然而,实现可以检测 ZMTP/1.0 实现并与之互操作。

如果实现不想与 ZMTP/1.0 对等方向后兼容,它应该使用上面语法中定义的签名。为了检测 ZMTP/1.0 对等方并与之互操作,实现应该在打开 TCP socket 后立即执行以下操作:

  • 发送一个由“%xFF 长度 %x7F”组成的 10 个八位字节的签名,其中“长度”是发送者身份的长度(0 或更多个八位字节)加 1。长度 MUST 是 8 个八位字节,采用网络字节序。

  • 从对方对等方读取第一个八位字节。

  • 如果此八位字节不是 %FF,则对方对等方正在使用 ZMTP/1.0。

  • 如果此八位字节是 %FF,则我们再读取九个八位字节,并检查最后一个八位字节(第 10 个)。如果最低有效位是 0,则对方对等方正在使用 ZMTP/1.0。

  • 如果最低有效位不是 0,则对方对等方正在使用 ZMTP/2.0 或更高版本。我们再读取两个八位字节,它们指示协议修订版本和对方对等方的 socket 类型。然后,我们使用 ZMTP/2.0 帧格式语法对该连接上的所有后续帧进行编码/解码。

  • 当我们检测到 ZMTP/1.0 对等方时,我们已经发送了 10 个八位字节,对方对等方将其解释为身份帧的开始。我们继续发送身份帧的体(零或更多个八位字节)。从那时起,我们使用 ZMTP/1.0 帧格式语法对该连接上的所有帧进行编码和解码。

安全

ZMTP/2.0 不尝试提供安全性,应用程序 MAY 在其之上添加安全层。