MQTT学习
- 1、在 ubuntu上安装MQTT
- 1.1、直接命令安装
- 1.2、通过源码安装
- 2、测试
- 2.1、本机测试
- 2.2、windowsMQTT工具测试
- 2.2.1、下载安装mqttfx
- 2.2.2、配置好mqttfx
- 2.2.3、测试
- 3、MQTT协议讲解
- 3.1、协议讲解
- 3.2、MQTT协议实现方式
- 3.3、网络传输与应用消息
- 3.4、MQTT客户端
- 3.5、MQTT服务器
- 3.6、MQTT协议中的订阅、主题、会话
- 一、订阅(Subscription)
- 二、会话(Session)
- 三、主题名(Topic Name)
- 四、主题筛选器(Topic Filter)
- 五、负载(Payload)
- 3.7、MQTT协议中的方法
- 3.8、MQTT协议数据包结构
- 一、固定报头
- 1.1、控制报文的类型如下表所示:
- 1.2、标志 Flags
- 1.3、剩余长度 Remaining Length
- 二、可变报头
- 2.1、报文标识符
- 三、有效载荷 Payload
- 3.9、MQTT控制报文
- 一、CONNECT – 连接服务端
- 1、固定报头
- 2、可变报头
- 二、CONNACK – 确认连接请求
- 三、 PUBLISH – 发布消息
- 四、PUBACK –发布确认
- 五、PUBREC – 发布收到(QoS 2,第一步)
- 六、PUBREL – 发布释放(QoS 2,第二步)
- 七、PUBCOMP – 发布完成(QoS 2,第三步)
- 八、SUBSCRIBE - 订阅主题
- 九、SUBACK – 订阅确认
- 十、UNSUBSCRIBE –取消订阅
- 十一、UNSUBACK – 取消订阅确认
- 十二、PINGREQ – 心跳请求
- 十三、PINGRESP – 心跳响应
- 十四、DISCONNECT –断开连接
- 4、linux环境 测试代码
- 4.1、客户端(订阅消息)
- 4.2、客户端(发布消息)
- 4.3、运行结果
1、在 ubuntu上安装MQTT
1.1、直接命令安装
- 引入mosquitto仓库并更新
# 注意 这一步不能忽略
sudo apt-add-repository ppa:mosquitto-dev/mosquitto-ppa
sudo apt-get update
- 执行以下命令安装mosquitto包
sudo apt-get install mosquitto
- 安装mosquitto开发包
sudo apt-get install mosquitto-dev
- 安装mosquitto客户端
sudo apt-get install mosquitto-clients
- 查询mosquitto是否正确运行
sudo service mosquitto status
图1-1 mosquitto运行状态
1.2、通过源码安装
- 安装snap
sudo apt install -y snapd snapcraft
- 下载源码
git clone https:///eclipse/mosquitto.git
- 编译源码
cd mosquitto
make
sudo make install
- 可能遇到的问题
问题1:
make[1]: xsltproc: Command not found
解决办法:
sudo apt-get install xsltproc
问题2:
I/O error : Attempt to load network entity
http://docbook.sourceforge.net/release/xsl/current/manpages/docbook.xsl
解决办法:
sudo apt-get install docbook
sudo apt-get install xmlto
2、测试
2.1、本机测试
#打开一个终端,执行以下命令订阅主题"mqtt"
mosquitto_sub -h localhost -t "mqtt" -v
图2-1 sub订阅
#打开另外一个终端,发布消息到主题 “mqtt”
mosquitto_pub -h localhost -t "mqtt" -m "Hello MQTT"
#现在你会看到消息被显示在前一个终端上了.
图2-2 pub推送
2.2、windowsMQTT工具测试
2.2.1、下载安装mqttfx
图2-3 mqttfx 界面
2.2.2、配置好mqttfx
图2-4 mqtt.fx配置界面
备注:
client ID 要点击自动生成一下
2.2.3、测试
- 第一步:点解connect
- 第二步:选择publish
- 第三步:输入主题
- 第四步:输入负载(内容)
- 第五步:点击publish按钮
图2-5 MQTT pub发布
图2-6 主机sub订阅
3、MQTT协议讲解
3.1、协议讲解
MQTT(消息队列遥测传输)是ISO 标准(ISO/IEC PRF 20922)下基于发布/订阅范式的消息协议。它工作在 TCP/IP协议族上,是为硬件性能低下的远程设备以及网络状况糟糕的情况下而设计的发布/订阅型消息协议,为此,它需要一个消息中间件 。
MQTT是一个基于客户端-服务器的消息发布/订阅传输协议。MQTT协议是轻量、简单、开放和易于实现的,这些特点使它适用范围非常广泛。在很多情况下,包括受限的环境中,如:机器与机器(M2M)通信和物联网(IoT)。其在,通过卫星链路通信传感器、偶尔拨号的医疗设备、智能家居、及一些小型化设备中已广泛使用。
特点:
1、使用发布/订阅消息模式,提供一对多的消息发布,解除应用程序耦合;
2、对负载内容屏蔽的消息传输;
3、使用 TCP/IP 提供网络连接;
4、有三种消息发布服务质量:
- “至多一次”(QoS 0),消息发布完全依赖底层 TCP/IP网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
- “至少一次”(QoS 1),确保消息到达,但消息重复可能会发生。
- “只有一次”(QoS 2),确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果。
5、小型传输,开销很小(固定长度的头部是 2 字节),协议交换最小化,以降低网络流量;
6、使用 Last Will 和 Testament 特性通知有关各方客户端异常中断的机制。
3.2、MQTT协议实现方式
实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:
- 发布者(Publish)
- 代理(Broker)(服务器)
- 订阅者(Subscribe)
其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者。
MQTT传输的消息分为:
- 主题(Topic): 可以理解为消息的类型,订阅者订阅(Subscribe)后,就会收到该主题的消息内容(payload)
- 负载(payload): 可以理解为消息的内容,是指订阅者具体要使用的内容。
它的基本模型如下:
图3-1 MQTT基本模型
3.3、网络传输与应用消息
MQTT会构建底层网络传输:它将建立客户端到服务器的连接,提供两者之间的一个有序的、无损的、基于字节流的双向传输。
当应用数据通过MQTT网络发送时,MQTT会把与之相关的服务质量(QoS)和主题名(Topic)相关连。
3.4、MQTT客户端
一个使用MQTT协议的应用程序或者设备,它总是建立到服务器的网络连接。客户端可以:
(1)发布其他客户端可能会订阅的信息;
(2)订阅其它客户端发布的消息;
(3)退订或删除应用程序的消息;
(4)断开与服务器连接。
3.5、MQTT服务器
MQTT服务器以称为“消息代理”(Broker),可以是一个应用程序或一台设备。它是位于消息发布者和订阅者之间,它可以:
(1)接受来自客户的网络连接;
(2)接受客户发布的应用信息;
(3)处理来自客户端的订阅和退订请求;
(4)向订阅的客户转发应用程序消息。
3.6、MQTT协议中的订阅、主题、会话
一、订阅(Subscription)
订阅包含主题筛选器(Topic Filter)和最大服务质量(QoS)。订阅会与一个会话(Session)关联。一个会话可以包含多个订阅。每一个会话中的每个订阅都有一个不同的主题筛选器。
二、会话(Session)
每个客户端与服务器建立连接后就是一个会话,客户端和服务器之间有状态交互。会话存在于一个网络之间,也可能在客户端和服务器之间跨越多个连续的网络连接。
三、主题名(Topic Name)
连接到一个应用程序消息的标签,该标签与服务器的订阅相匹配。服务器会将消息发送给订阅所匹配标签的每个客户端。
四、主题筛选器(Topic Filter)
一个对主题名通配符筛选器,在订阅表达式中使用,表示订阅所匹配到的多个主题。
五、负载(Payload)
消息订阅者所具体接收的内容。
3.7、MQTT协议中的方法
MQTT协议中定义了一些方法(也被称为动作),来于表示对确定资源所进行操作。这个资源可以代表预先存在的数据或动态生成数据,这取决于服务器的实现。通常来说,资源指服务器上的文件或输出。主要方法有:
(1)Connect。等待与服务器建立连接。
(2)Disconnect。等待MQTT客户端完成所做的工作,并与服务器断开TCP/IP会话。
(3)Subscribe。等待完成订阅。
(4)UnSubscribe。等待服务器取消客户端的一个或多个topics订阅。
(5)Publish。MQTT客户端发送消息请求,发送完成后返回应用程序线程。
3.8、MQTT协议数据包结构
MQTT控制报文的结构基本由三部分组成:
- 固定报头(Fixed header): 存在于所有MQTT数据包中,表示数据包类型及数据包的分组类标识。
- 可变报头(Variable header):存在于部分MQTT数据包中,数据包类型决定了可变头是否存在及其具体内容。
- 有效载荷(Payload):存在于部分MQTT数据包中,表示客户端收到的具体内容。
一、固定报头
每个MQTT控制报文都包含一个固定报头。固定报头的格式如下。
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT控制报文的类型 | 用于指定控制报文的标志位 | ||||||
byte 2... | 剩余长度 |
1.1、控制报文的类型如下表所示:
位置: 第1个字节,二进制位7-4。
表示为4位无符号值,这些值的定义。
名字 | 值 | 流动方向 | 描述 |
Reserved | 0 | 禁止 | 保留 |
Connect | 1 | 客户端到服务端 | 客户端到服务端的连接请求 |
ConnACK | 2 | 服务端到客户端 | 服务端对连接请求的响应 |
Publish | 3 | 两个方向都允许 | 发布消息(QoS0) |
puback | 4 | 两个方向都允许 | 对QoS1发布消息的回应 |
pubRec | 5 | 两个方向都允许 | 收到发布消息(QoS2保证传输第一步) |
pubRel | 6 | 两个方向都允许 | 释放发布消息(QoS2保证传输第二步) |
pubComp | 7 | 两个方向都允许 | 完成发布消息(QoS2保证传输第三步) |
subscribe | 8 | 客户端到服务端 | 客户端订阅请求 |
subBack | 9 | 服务端到客户端 | 订阅请求的确认 |
unsubscribe | 10 | 客户端到服务端 | 客户端取消订阅请求 |
unsubBack | 11 | 服务端到客户端 | 取消订阅请求确认 |
pingReq | 12 | 客户端到服务端 | Ping(心跳)请求(保持连接) |
pingResp | 13 | 服务端到客户端 | Ping(心跳)响应 |
disconnect | 14 | 客户端到服务端 | 客户端断开连接 |
reserved | 15 | 禁止 | 保留 |
1.2、标志 Flags
固定报头第1个字节的剩余的4位 [3-0]包含每个MQTT控制报文类型特定的标志,如下表所示。
如果收到非法的标志,接收者必须关闭网络连接。
控制报文 | 固定报头标志 | Bit 3 | Bit 2 | Bit 1 | Bit 0 |
CONNECT | Reserved | 0 | 0 | 0 | 0 |
CONNACK | Reserved | 0 | 0 | 0 | 0 |
PUBLISH | Used in MQTT 3.1.1 | DUP1 | QoS2 | QoS2 | RETAIN3 |
PUBACK | Reserved | 0 | 0 | 0 | 0 |
PUBREC | Reserved | 0 | 0 | 0 | 0 |
PUBREL | Reserved | 0 | 0 | 1 | 0 |
PUBCOMP | Reserved | 0 | 0 | 0 | 0 |
SUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
SUBACK | Reserved | 0 | 0 | 0 | 0 |
UNSUBSCRIBE | Reserved | 0 | 0 | 1 | 0 |
UNSUBACK | Reserved | 0 | 0 | 0 | 0 |
PINGREQ | Reserved | 0 | 0 | 0 | 0 |
PINGRESP | Reserved | 0 | 0 | 0 | 0 |
DISCONNECT | Reserved | 0 | 0 | 0 | 0 |
- DUP1 =控制报文的重复分发标志
- QoS2 = PUBLISH报文的服务质量等级
- RETAIN3 = PUBLISH报文的保留标志
1.3、剩余长度 Remaining Length
位置: 从第2个字节开始。
剩余长度(Remaining Length)表示当前报文剩余部分的字节数,包括可变报头和负载的数据。剩余长度不包括用于编码剩余长度字段本身的字节数。
剩余长度字段使用一个变长度编码方案,对小于128的值它使用单字节编码。更大的值按下面的方式处理。低7位有效位用于编码数据,最高有效位用于指示是否有更多的字节。因此每个字节可以编码128个数值和一个延续位(continuation bit)。剩余长度字段最大4个字节。
非规范评注
例如,十进制数64会被编码为一个字节,数值是64,十六进制表示为0x40,。十进制数字321(=65+2*128)被编码为两
个字节,最低有效位在前。第一个字节是 65+128=193。注意最高位为1表示后面至少还有一个字节。第二个字节是1。
非规范评注
这允许应用发送最大256MB(268,435,455)大小的控制报文。这个数值在报文中的表示是:0xFF,0xFF,0xFF,0x7F。
剩余长度字段的大小展示了剩余长度字段所表示的值随字节增长。
字节数 | 最小值 | 最大值 |
1 | 0 (0x00) | 127 (0x7F) |
2 | 128 (0x80, 0x01) | 16 383 (0xFF, 0x7F) |
3 | 16 384 (0x80, 0x80, 0x01) | 2 097 151 (0xFF, 0xFF, 0x7F) |
4 | 2 097 152 (0x80, 0x80, 0x80, 0x01) | 268 435 455 (0xFF, 0xFF, 0xFF, 0x7F) |
分别表示(每个字节的低7位用于编码数据,最高位是标志位):
- 1个字节时,从0(0x00)到127(0x7f)
- 2个字节时,从128(0x80,0x01)到16383(0Xff,0x7f)
- 3个字节时,从16384(0x80,0x80,0x01)到2097151(0xFF,0xFF,0x7F)
- 4个字节时,从2097152(0x80,0x80,0x80,0x01)到268435455(0xFF,0xFF,0xFF,0x7F)
非规范评注
非负整数X使用变长编码方案的算法如下:
do
encodedByte = X MOD 128
X = X DIV 128
// if there are more data to encode, set the top bit of this byte
if ( X > 0 )
encodedByte = encodedByte OR 128
endif
'output' encodedByte
while ( X > 0 )
备注:
MOD是模运算,DIV是整数除法,OR是位操作或(C语言中分别是%,/,|)
非规范评注
非负整数X使用变长编码方案的算法如下:
multiplier = 1
value = 0
do
encodedByte = 'next byte from stream'
value += (encodedByte AND 127) * multiplier
if (multiplier > 128*128*128)
throw Error(Malformed Remaining Length)
multiplier *= 128
while ((encodedByte AND 128) != 0)
备注:
AND是位操作与(C语言中的&)
这个算法终止时,value包含的就是剩余长度的值。
二、可变报头
某些MQTT控制报文包含一个可变报头部分。它在固定报头和负载之间。可变报头的内容根据报文类型的不同而不同。可变报头的报文标识符(Packet Identifier)字段存在于在多个类型的报文里。
2.1、报文标识符
Bit | 7 - 0 |
byte 1 | 报文标识符 MSB |
byte 2 | 报文标识符 LSB |
很多控制报文的可变报头部分包含一个两字节的报文标识符字段。这些报文是PUBLISH(QoS > 0时), PUBACK,PUBREC,PUBREL,PUBCOMP,SUBSCRIBE, SUBACK,UNSUBSCRIBE,UNSUBACK。
SUBSCRIBE,UNSUBSCRIBE和PUBLISH(QoS大于0)控制报文必须包含一个非零的16位报文标识符(Packet Identifier)。
客户端每次发送一个新的这些类型的报文时都必须分配一个当前未使用的报文标识符。如果一个客户端要重发这个特殊的控制报文,在随后重发那个报文时,它必须使用相同的标识符。当客户端处理完这个报文对应的确认后,这个报文标识符就释放可重用。QoS 1的PUBLISH对应的是PUBACK,QoS 2的PUBLISH对应的是PUBCOMP,与SUBSCRIBE或UNSUBSCRIBE对应的分别是SUBACK或UNSUBACK 。发送一个QoS 0的PUBLISH报文时,相同的条件也适用于服务端。
QoS等于0的PUBLISH报文不能
包含报文标识符。
PUBACK, PUBREC, PUBREL报文必须包含与最初发送的PUBLISH报文相同的报文标识符 。类似地,SUBACK和UNSUBACK必须包含在对应的SUBSCRIBE和UNSUBSCRIBE报文中使用的报文标识符。
需要报文标识符的控制报文在 包含报文标识符的控制报文
中列出。
表格 2.5 -包含报文标识符的控制报文
控制报文 | 报文标识符字段 |
CONNECT | 不需要 |
CONNACK | 不需要 |
PUBLISH | 需要(如果QoS > 0) |
PUBACK | 需要 |
PUBREC | 需要 |
PUBREL | 需要 |
PUBCOMP | 需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
客户端和服务端彼此独立地分配报文标识符。因此,客户端服务端组合使用相同的报文标识符可以实现并发的消息交换。非规范评注
客户端发送标识符为0x1234的PUBLISH报文,它有可能会在收到那个报文的PUBACK之前,先收到服务端发送的另一个不同的但是报文标识符也为0x1234的PUBLISH报文。
Client | Server |
PUBLISH | Packet Identifier=0x1234— |
–PUBLISH | Packet Identifier=0x1234 |
PUBACK | Packet Identifier=0x1234— |
–PUBACK | Packet Identifier=0x1234 |
三、有效载荷 Payload
某些MQTT控制报文在报文的最后部分包含一个有效载荷,这将在第三章论述。对于PUBLISH来说有效载荷就是应用消息。表格包含有效载荷的控制报文
列出了需要有效载荷的控制报文。
表格 2.6 – 包含有效载荷的控制报文
控制报文 | 有效载荷 |
CONNECT | 需要 |
CONNACK | 不需要 |
PUBLISH | 可选 |
PUBACK | 不需要 |
PUBREC | 不需要 |
PUBREL | 不需要 |
PUBCOMP | 不需要 |
SUBSCRIBE | 需要 |
SUBACK | 需要 |
UNSUBSCRIBE | 需要 |
UNSUBACK | 不需要 |
PINGREQ | 不需要 |
PINGRESP | 不需要 |
DISCONNECT | 不需要 |
3.9、MQTT控制报文
一、CONNECT – 连接服务端
客户端到服务端的网络连接建立后,客户端发送给服务端的第一个报文必须是CONNECT报文
。
在一个网络连接上,客户端只能发送一次CONNECT报文
。服务端必须将客户端发送的第二个CONNECT报文当作协议违规处理并断开客户端的连接。
有效载荷包含一个或多个编码的字段。包括客户端的唯一标识符,Will主题,Will消息,用户名和密码
。除了客户端标识
之外,其它的字段都是可选的,基于标志位来决定可变报头中是否需要包含这些字段。
参考链接
1、固定报头
图例 3.1 –CONNECT报文的固定报头
Bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
byte 1 | MQTT报文类型 (1) | Reserved 保留位 | ||||||
0 | 0 | 0 | 1 | 0 | 0 | 0 | 0 | |
byte 2... | 剩余长度 |
剩余长度字段
剩余长度等于可变报头的长度(10字节)加上有效载荷的长度。编码方式见 3.8-1.3节的说明。
2、可变报头
CONNECT报文的可变报头按下列次序包含四个字段:协议名(Protocol Name),协议级别(Protocol Level),连接标志(Connect Flags)和保持连接(Keep Alive)。
- 协议名
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议名 | |||||||||
byte 1 | 长度 MSB (0) | 0 | 0 | 0 | 0 | 0 | 0 | 0 | 0 |
byte 2 | 长度 LSB (4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
byte 3 | ‘M’ | 0 | 1 | 0 | 0 | 1 | 1 | 0 | 1 |
byte 4 | ‘Q’ | 0 | 1 | 0 | 1 | 0 | 0 | 0 | 1 |
byte 5 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
byte 6 | ‘T’ | 0 | 1 | 0 | 1 | 0 | 1 | 0 | 0 |
协议名是表示协议名 MQTT 的UTF-8编码的字符串。MQTT规范的后续版本不会改变这个字符串的偏移和长度。
如果协议名不正确服务端可以断开客户端的连接,也可以按照某些其它规范继续处理CONNECT报文。对于后一种情况,按照本规范,服务端不能继续处理CONNECT报文 。
- 协议级别
协议级别字节构成
说明 | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 | |
协议级别 | |||||||||
byte 7 | Level(4) | 0 | 0 | 0 | 0 | 0 | 1 | 0 | 0 |
客户端用8位的无符号值表示协议的修订版本。对于3.1.1版协议,协议级别字段的值是4(0x04)。如果发现不支持的协议级别,服务端必须给发送一个返回码为0x01(不支持的协议级别)的CONNACK报文响应CONNECT报文,然后断开客户端的连接。
- 连接标志
连接标志字节包含一些用于指定MQTT连接行为的参数。它还指出有效载荷中的字段是否存在。
二、CONNACK – 确认连接请求
服务端发送CONNACK报文响应从客户端收到的CONNECT报文。服务端发送给客户端的第一个报文必须是CONNACK 。
如果客户端在合理的时间内没有收到服务端的CONNACK报文,客户端应该关闭网络连接。合理的时间取决于应用的类型和通信基础设施。
参考链接
三、 PUBLISH – 发布消息
PUBLISH控制报文是指从客户端向服务端或者服务端向客户端传输一个应用消息。
参考链接
四、PUBACK –发布确认
PUBACK报文是对QoS 1等级的PUBLISH报文的响应。
参考链接
五、PUBREC – 发布收到(QoS 2,第一步)
PUBREC报文是对QoS等级2的PUBLISH报文的响应。它是QoS 2等级协议交换的第二个报文。
参考链接
六、PUBREL – 发布释放(QoS 2,第二步)
PUBREL报文是对PUBREC报文的响应。它是QoS 2等级协议交换的第三个报文。
参考链接
七、PUBCOMP – 发布完成(QoS 2,第三步)
PUBCOMP报文是对PUBREL报文的响应。它是QoS 2等级协议交换的第四个也是最后一个报文。
参考链接
八、SUBSCRIBE - 订阅主题
客户端向服务端发送SUBSCRIBE报文用于创建一个或多个订阅。每个订阅注册客户端关心的一个或多个主题。为了将应用消息转发给与那些订阅匹配的主题,服务端发送PUBLISH报文给客户端。SUBSCRIBE报文也(为每个订阅)指定了最大的QoS等级,服务端根据这个发送应用消息给客户端。
参考链接
九、SUBACK – 订阅确认
服务端发送SUBACK报文给客户端,用于确认它已收到并且正在处理SUBSCRIBE报文。
SUBACK报文包含一个返回码清单,它们指定了SUBSCRIBE请求的每个订阅被授予的最大QoS等级。
参考链接
十、UNSUBSCRIBE –取消订阅
客户端发送UNSUBSCRIBE报文给服务端,用于取消订阅主题。
参考链接
十一、UNSUBACK – 取消订阅确认
服务端发送UNSUBACK报文给客户端用于确认收到UNSUBSCRIBE报文。
参考链接
十二、PINGREQ – 心跳请求
客户端发送PINGREQ报文给服务端的。用于:
在没有任何其它控制报文从客户端发给服务的时,告知服务端客户端还活着。
- 请求服务端发送 响应确认它还活着。
- 使用网络以确认网络连接没有断开。
- 保持连接(Keep Alive)处理中用到这个报文。
十三、PINGRESP – 心跳响应
服务端发送PINGRESP报文响应客户端的PINGREQ报文。表示服务端还活着。
保持连接(Keep Alive)处理中用到这个报文。
参考链接
十四、DISCONNECT –断开连接
DISCONNECT报文是客户端发给服务端的最后一个控制报文。表示客户端正常断开连接。
参考链接
4、linux环境 测试代码
备注: 编译源码,条件是在建立在通过mqtt源码安装的mosquitto,否则会提示找不到<mosquitto.h>头文件。
4.1、客户端(订阅消息)
#include <stdio.h>
#include <stdlib.h>
#include <mosquitto.h>
#include <string.h>
#define HOST "localhost" //可以改为自己MQTT的服务器地址 如:#define HOST "106.xxx.xxx.xxx"
#define PORT 1883 //端口号
#define KEEP_ALIVE 60
bool session = true;
void my_message_callback(struct mosquitto *mosq, void *userdata, const struct mosquitto_message *message)
{
if(message->payloadlen){
printf("%s %s", message->topic, message->payload);
}else{
printf("%s (null)\n", message->topic);
}
fflush(stdout);
}
void my_connect_callback(struct mosquitto *mosq, void *userdata, int result)
{
int i;
if(!result){
/* Subscribe to broker information topics on successful connect. */
mosquitto_subscribe(mosq, NULL, "mqtt", 2); //topic 主题:"mqtt"
}else{
fprintf(stderr, "Connect failed\n");
}
}
void my_subscribe_callback(struct mosquitto *mosq, void *userdata, int mid, int qos_count, const int *granted_qos)
{
int i;
printf("Subscribed (mid: %d): %d", mid, granted_qos[0]);
for(i=1; i<qos_count; i++){
printf(", %d", granted_qos[i]);
}
printf("\n");
}
void my_log_callback(struct mosquitto *mosq, void *userdata, int level, const char *str)
{
/* Pring all log messages regardless of level. */
printf("%s\n", str);
}
int main(void)
{
struct mosquitto *mosq = NULL;
//libmosquitto 库初始化
mosquitto_lib_init();
//创建mosquitto客户端
mosq = mosquitto_new(NULL,session,NULL);
if(!mosq){
printf("create client failed..\n");
mosquitto_lib_cleanup();
return 1;
}
//设置回调函数,需要时可使用
//mosquitto_log_callback_set(mosq, my_log_callback);
mosquitto_connect_callback_set(mosq, my_connect_callback);
mosquitto_message_callback_set(mosq, my_message_callback);
//mosquitto_subscribe_callback_set(mosq, my_subscribe_callback);
//客户端连接服务器
if(mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE)){
fprintf(stderr, "Unable to connect.\n");
return 1;
}
//循环处理网络消息
mosquitto_loop_forever(mosq, -1, 1);
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
4.2、客户端(发布消息)
#include <stdio.h>
#include <stdlib.h>
#include <mosquitto.h>
#include <string.h>
#define HOST "localhost" //可以改为自己MQTT的服务器地址 如:#define HOST "l06.xxx.xxx.xxx"
#define PORT 1883 //端口号
#define KEEP_ALIVE 60
#define MSG_MAX_SIZE 512
bool session = true;
int main(void)
{
char buff[MSG_MAX_SIZE];
struct mosquitto *mosq = NULL;
//libmosquitto 库初始化
mosquitto_lib_init();
//创建mosquitto客户端
mosq = mosquitto_new(NULL,session,NULL);
if(!mosq){
printf("create client failed..\n");
mosquitto_lib_cleanup();
return 1;
}
//连接服务器
if(mosquitto_connect(mosq, HOST, PORT, KEEP_ALIVE)){
fprintf(stderr, "Unable to connect.\n");
return 1;
}
//开启一个线程,在线程里不停的调用 mosquitto_loop() 来处理网络信息
int loop = mosquitto_loop_start(mosq);
if(loop != MOSQ_ERR_SUCCESS)
{
printf("mosquitto loop error\n");
return 1;
}
while(fgets(buff, MSG_MAX_SIZE, stdin) != NULL)
{
/*发布消息*/
mosquitto_publish(mosq,NULL,"mqtt",strlen(buff)+1,buff,0,0); //topic 主题:"mqtt"
memset(buff,0,sizeof(buff));
}
mosquitto_destroy(mosq);
mosquitto_lib_cleanup();
return 0;
}
注意
在编译程序时需加上-lmosquitto链接。如:
gcc -o mosquitto_client_sub mosquitto_client_sub.c -lmosquitto
4.3、运行结果
- 执行发布程序
./mosquitto_client_push
图4-1 发布
- 通过mqttfx发布消息
图4-2 发布mqttfx
- 执行订阅程序
./mosquitto_client_sub
图4-3 订阅