# 协议定义 如无特殊说明: - 本协议中所有字符串均采用**ASCII编码**表示为二进制值, 如`"ABC"`表示字节序列`[65, 66, 67]`, 而`"ABC\0"`表示`[65, 66, 67, 0]` - 本协议中所有整数均采用**网络字节序的2的补码表示** ## (I) 握手 ### 1. 请求 客户端向服务器发起TCP连接。在TCP连接建立后,客户端应首先发送**消息1**。 #### 消息1 ``` +--------+------------------+------------------+ | "PSMB" | version (uint32) | options (uint32) | +--------+------------------+------------------+ ``` 载荷说明: - `"PSMB"`:协议头 - `version`:版本号,服务器据此判断服务器和客户端双方的协议是否兼容 - `options`:初始参数。目前,所有位均保留供将来使用,应设置为全零 ### 2. 响应 1. 服务端读取4个字节,并判断是否为`"PSMB"`。如果不是,立刻断开连接。 2. 服务端读出一个4字节无符号数,记为`ver`,判断`ver`是否为其兼容的版本号。如果不是,返回**消息2**,并立刻断开连接。 3. 服务端读出32位长的比特数组,记为`options`,判断`options`的比特是否全部为零。如果不是,立刻断开连接。如果是,返回**消息3**。 #### 消息2 ``` +--------------------------+ | "UNSUPPORTED PROTOCOL\0" | +--------------------------+ ``` 载荷说明: - `"UNSUPPORTED PROTOCOL\0"`:表示该协议版本不受服务端支持 #### 消息3 ``` +--------+------------------+ | "OK\0" | options (uint32) | +--------+------------------+ ``` 载荷说明: - `"OK\0"`:表示服务端接受了客户端的传入连接,连接成功建立 - `options`:服务端向客户端发送的选项。在当前版本,该字段应为全零 ### 3. 客户端处理 - 客户端读入一个以`\0`结尾的字符串,如果是`"UNSUPPORTED PROTOCOL\0"`,断开连接; 如果是`"OK\0"`,且继续读取32个比特全为0,则进入**状态(II)**;如果不是,断开连接 ## (II) 模式选择 ### 1. 模式选择 客户端选择本连接的类型,如果是`PUBLISH`,则发送**消息4**;如果是`SUBSCRIBE`,则发送**消息5**。 #### 消息4 ``` +-------+--------------------------------------+ | "PUB" | topic_id (str, terminated with '\0') | +-------+--------------------------------------+ ``` 载荷说明: - `"PUB"`:表示该连接为`PUBLISH`类型。 客户端发送要发布的消息,服务端接收消息, 并转发给订阅该话题的其他客户端 - `topic_id`:该连接发布的消息的话题标识符,是一个字符串 #### 消息5 ``` +-------+------------------+----------------------------------------+-----+ | "SUB" | options (uint32) | id_pattern (str, terminated with '\0') | ... | +-------+------------------+----------------------------------------+-----+ ``` 载荷说明: - `"SUB"`:表示该连接为`SUBSCRIBE`类型。 客户端提供一个匹配模式串,服务端将来自所有标识符匹配该模式串的话题的消息转发给客户端。 - `id_pattern`:模式串,一个**PCRE正则表达式**字符串。 - `...`:可选载荷。按照从最低有效位到最高有效位的顺序遍历`options`中的每一位,如果该位为1,则读入一个选项。 + 第0位:`ALLOW_HISTORY`。如果该为为1,则服务器将向客户端发送它没有接收过的所有消息,即有记忆功能。 选项: ``` +------------------------+ | subscriber_id (uint64) | +------------------------+ ``` ### 2. 响应 服务端接收客户端的命令后,判断命令是否合法:读取3个字节,如果既不是`"PUB"`也不是`"SUB"`,返回**消息6**并断开连接。 否则,如果接受该命令,返回**消息7**并进入**状态(III)**;如果拒绝该命令,返回**消息8**并保持在该状态。 #### 消息6 ``` +-----------------+ | "BAD COMMAND\0" | +-----------------+ ``` #### 消息7 ``` +--------+ | "OK\0" | +--------+ ``` #### 消息8 ``` +------------+--------------------------------------------------------------------------------------------+ | "FAILED\0" | error_message (str, terminated with '\0', read up to 128 Bytes including terminating '\0') | +------------+--------------------------------------------------------------------------------------------+ ``` ## (III) 消息交换 如果是`PUBLISH`模式,客户端向服务器发送消息,记主动方为客户端;如果是`SUBSCRIBE`模式,服务器向客户端发送消息,记主动方为服务端。 无论是在哪种模式,主动方发送的消息都应为**消息9**或**消息10**的一种。 #### 消息9 ``` +-------+-------------------------+----------------------------------+ | "MSG" | message_length (uint64) | message (`message_length` bytes) | +-------+-------------------------+----------------------------------+ ``` 载荷说明: - `"MSG"`:表示该消息是一个携带一个上层消息的报文 - `message_length`:上层消息长度 - `message`:上层消息,长度为`message_length`字节 #### 消息10 ``` +-------+ | "NOP" | +-------+ ``` 载荷说明: - `"NOP"`:表示该消息是一个空指令,什么都不做(当经过*NAT设备*或*防火墙*时,可定时发送此消息以保持TCP连接)。接收方总是返回一个**消息12** #### 消息11 ``` +-------+ | "BYE" | +-------+ ``` 载荷说明: - `"BYE"`:表示主动方将要终止连接。接收到此消息后,被动方(主动方的对方)也要终止连接 #### 消息12 ``` +-------+ | "NIL" | +-------+ ``` 载荷说明: - `"NIL":表示一个空消息。接收方应总是丢弃该消息