# TCP 协议网关

# 协议介绍

EMQX 提供 emqx-tcp 模块作为一个靠近端侧的一个接入模块,按照其功能逻辑和整个系统的关系,将整个消息交换的过程可以分成三个部分:终端侧,平台侧和其它侧:

|<-- Terminal -->|<--------- Broker Side --------->|<---  Others  --->|
|<-    Sid e   ->|                                 |<--    Side    -->|

+---+                                                PUB  +-----------+
| D |  INCOMING  +----------+    PUB     +---------+   -->| subscriber|
| E |----------->|          |----------->|         |--/   +-----------+
| V |            | emqx-tcp |            |  EMQX  |
| I |<-----------|          |<-----------|         |<--   +-----------+
| C |  OUTGOING  +----------+    PUB     +---------+   \--| publisher |
| E |                                                PUB  +-----------+
+---+
1
2
3
4
5
6
7
8
9
10
11
  1. 终端侧,通过本模块定义的 TCP 私有协议进行接入,然后实现数据的上报,或者接收下行的消息。
  2. 平台侧,主体是 emqx-tcp 模块和 EMQX 系统。emqx-tcp 负责报文的编解码,代理订阅下行主题。实现将上行消息转为 EMQX 系统中的 MQTT 消息 PUBLISH 到整个系统中;将下行的 MQTT 消息转化为 TCP 私有协议的报文结构,下发到终端。
  3. 其它侧,可以对 2 中出现的上行的 PUBLISH 消息的主题进行订阅,以接收上行消息。或对发布消息到具体的下行的主题,以发送数据到终端侧。

# 创建模块

打开 EMQX Dashboard (opens new window),点击左侧的 “模块” 选项卡,选择添加:

image-20200927213049265

选择 TCP 私有协议接入网关:

image-20200927213049265

配置相关基础参数:

image-20200927213049265

添加监听端口:

image-20200927213049265

配置监听参数:

image-20200927213049265

点击确认到配置参数页面:

image-20200927213049265

点击添加后,模块添加完成: image-20200927213049265

# 配置参数

配置项说明
空闲超时时间闲置时间。超过该时间未收到 CONNECT 帧, 将直接关闭该 TCP 连接
上行主题上行主题。上行消息到 EMQ 系统中的消息主题%c: 接入客户端的 ClientId,%u: 接入客户端的 Username
下行主题下行主题。上行消息到 EMQ 系统中的消息主题%c: 接入客户端的 ClientId,%u: 接入客户端的 Username
报文最大长度最大处理的单个 TCP 私有协议报文大小
强制 GC 策略强制 GC, 当进程已处理 1000 消息或发送数据超过 1M
强制关闭策略强制关闭连接, 当进程堆积 8000 消息或堆栈内存超过 800M

# 私有 TCP 协议 - v1

# 设计准则

私有 TCP 协议的设计原则有三:

  1. 轻量: 尽量减少头部、控制字段的字节大小
  2. 简洁: 私有 TCP 协议,主要功能定位在透传上层应用/协议的数据报文。所以功能应当简洁,专注透明传输即可
  3. 可靠: 保证消息有序可达

# 报文结构

报文主要有俩部分构成: 固定头部(FixedHeader) + 有效载荷(Payload)

其中固定头部固定 1 字节;有效载荷为变长,且前面有 2 个字节标识整个 Payload 的长度:

         1 Byte         2 Bytes        N Bytes
    +--------------+----------------+--------------+
    | Fixed Header | Len of Payload |  Payload     |
    +--------------+----------------+--------------+
1
2
3
4

部分类型报文中不含 Payload; 则整个报文仅只有 1 个字节的固定头部

# 数据类型

本协议设计到的数据类型

NameBytesDescription
UINT(x)x固定 x 字节的 无符号整型
BIN(n)变长带 2 字节标示长度的变长二进制。n 取值为 0 到 65535 内容为空时,需使用 2 个字节,来标识长度值0
STR(n)变长带 2 字节标示长度的变长字符串。n 取值为 0 到 65535; 空串表达方式同上。
BIN_不带长度标识的二进制串

# 固定头部

固定头部有俩部分组成: 帧类型标志位

       7     6     5     4    3    2    1    0
    +------------------------------------------+
    |        4 Bits        |      4 Bits       |
    +------------------------------------------+
    |<--   Frame Type   -->|<--    Flags    -->|
1
2
3
4
5

帧类型(Frame Type) 有以下几种可选值

NameValueDirection of FlowDescription
CONNECT1Client --> Server连接报文
CONNACK2Server --> Client连接应答
DATATRANS3Client <==> Server透明传输
PING4Client --> Server心跳报文
PONG5Server --> Client心跳应答
DISCONNECT6Client --> Server主动断开连接
Reserved7-15保留保留字段

标志位(Flags) 针对每个类型的报文,标志位代表的含义都不相同。

# 报文详解

# CONNECT 帧

连接报文. 帧类型为 2#0001. 标志位 4 Bits 代表协议 版本号(Version) 目前为 1 即 2#0001。因此 CONNECT 帧固定头部为 0x11

而,Payload 中包含连接用的所有字段。则必须按照以下顺序给出,否则为非法报文,立即断开 TCP 链接:

    Len       Keepalive[x] ClientId[x]  Username  Password
    UINT(2)   UINT(1)      STR(n)       STR(n)    STR(n)
1
2

其中 Keepalive 和 ClientId 为必填字段;Username 与 Password 可不带。

因此,一个 Keepalive 为 60; ClientId 为 'abcd'; Username 和 Password 均为空时,报文的内容为:

    0x11 00 07 3c 00 04 61 62 63 64
1

若是 Username 和 Password 不为空且假设都为 'abcd' 的情况下,报文内容为:

    0x11 00 13 3c 00 04 61 62 63 64 00 04 61 62 63 64 00 04 61 62 63 64
1

# CONNACK 帧

连接应答报文. 帧类型为 2#0010. 标志位 4 Bits 为应答连接结果(ACK Code)。可以为以下枚举值:

NameValueDescription
SUCCESSFUL0连接成功
AUTHFAILED1认证失败
ILLEGALVER2不支持的协议版本
Reserved3-15保留字段

而,Payload 字段,为连接应答后传递的 Message; 该串可为空串。

Message
STR(n)
1
2

所以,当连接成功时,并返回 Connect Sucessfully 时,报文内容为:

    0x20 00 14 43 6f 6e 6e 65 63 74 20 53 75 63 63 65 73 73 66 75 6c 6c 79
1

若是,返回 认证失败 且 Message 为空时::

    0x21 00 00
1

# DATATRANS 帧

数据传输帧. 帧类型为 2#0011. 标志位 前 2 Bits 表达 消息质量等级(Qos) 目前恒为0;后两位为保留位。所以DATATRANS 帧固定头部恒为 0x30

Payload 内容为为透传的 数据字段

Len     Payload
UINT(2) BIN

1
2
3

注:由于 Len 固定为 2 字节,所以最大仅支持 65535 字节的负载。

因此,如果透传 abcd 这个字符串时,该报文的内容为:

    0x30 00 04 61 62 63 63

1
2

# PING 帧

心跳帧. 帧类型为 2#0100. 标志位 Flags 固定为 0。即固定头部 固定为:0x40

Payload 为空

因此,一个 PING 帧仅有一个字节:

    0x40

1
2

# PONG 帧

心跳应答帧. 帧类型为 2#0101. 标志位 Flags 固定为 0。即固定头部 固定为:0x50

Payload 为空

因此,一个 PONG 帧仅有一个字节:

    0x50
1

# DISCONNECT 帧

断开连接帧. 帧类型为 2#0111. Flags 为空。即固定头部 固定为:0x60

Payload 为空

因此,DISCONNECT 帧仅有 1 个字节:

    0x60
1