自己动手实现MQTT协议
写在前面
前段时间弄IoT相关的东西,系统学习了一下 MQTT 协议,在此分享出来。
本文先是对 MQTT 协议做了简单的介绍;接着是对 MQTT协议的内容做了较为全面的解读;最后使用 Python 语言去实现一个简单的 MQTT 客户端和服务器。
简介
MQTT 全称是 Message Queue Telemetry Transport,翻译成中文意思是“遥测传输协议”。它最先是由IBM提出,是一种基于 **TCP ** 协议,具有简单、轻量等优点,特别适合于受限环境(带宽低、网络延迟高、网络通信不稳定)的消息分发。MQTT 协议有 3.x, 5.x 等多个版本,目前最常用的版本是 v3.1.1 ,本文也是对此版本的协议进行的解读。MQTT 协议已纳入ISO标准 (ISO/IEC PRF 20922),现今主流的 IoT 平台都支持该协议。
快速开始
MQTT 是一种发布-订阅协议,这意味着:
- 客户端(Client)可以向服务端(Broker) 订阅(Subscribe)自己感兴趣的主题(Topic);
- 客户端还可以向服务端发布(Publish)关于某个主题的信息(主题不需要提前创建,发布消息时指定即可);
- 服务端在收到客户端发布的消息后,会将该消息转发给订阅了该主题的其他客户端。
我们可以在自己的电脑上运行一个 MQTT 的服务端,和多个 MQTT 的客户端来体验这一过程。
MQTT 服务端有很多可以选择。这里我们使用 Mosquitto,按照其官方文档的说明安装即可,这里不多做介绍。
Mac 用户可以用以下命令安装并启动 Mosquitto:
brew install mosquitto
brew services start mosquitto
Mosquitto 提供了命令行工具 mosquitto_sub 和 mosquitto_pub ,它们可用来向服务端订阅主题 和发布消息。
在一个命令行窗口中,执行以下命令去订阅名为 “foo” 的主题:
mosquitto_sub -h 127.0.0.1 -p 1883 -t foo -q 2
在另一个命令行窗口中,执行以下命令发布消息 “Hello, MQTT” 到 “foo” 主题:
mosquitto_pub -h 127.0.0.1 -p 1883 -t foo -q 2 -m 'Hello, MQTT'
最终我们将看到,在第一个命令行窗口中,打印出了消息 “Hello, MQTT”。这意味着,第一个客户端在主题 “foo” 上,收到了第二个客户端发布的消息。
协议详解
数据包整体格式
从整体上看,数据包分为3个部分:一个是固定头部,它是一定存在的;另一个是可变头部,它不一定存在;剩下一个是载荷,它也不一定存在。数据采用大端方式存储。
+----------------------------+
| |
| 固 定 头 部 (必 需 ) |
| |
+----------------------------+
| |
| 可 变 头 部 (非 必 需) |
| |
+----------------------------+
| |
| 载 荷 (非 必 需 ) |
| |
+----------------------------+
固定头部(Fixed header)
固定头部格式如下:
+---------------------------------------------------------+
| bit | 7 | 6 | 5 | 4 | 3 | 2 | 1 | 0 |
+---------------------------------------------------------+
| byte1 | Packet type | Flags |
+---------------------------------------------------------+
| byte2...| Remaining Length |
+---------------------------------------------------------+
包类型(Packet type)
| Name | Value | Direction of flow | Description |
|---|---|---|---|
| Reserved | 0 | Forbidden | Reserved |
| CONNECT | 1 | Client to Server | Client request to connect to Server |
| CONNACK | 2 | Server to Client | Connect acknowledgment |
| PUBLISH | 3 | Client to Server or Server to Client | Publish message |
| PUBACK | 4 | Client to Server or Server to Client | Publish acknowledgment |
| PUBREC | 5 | Client to Server or Server to Client | Publish received (assured delivery part 1) |
| PUBREL | 6 | Client to Server or Server to Client | Publish release (assured delivery part 2) |
| PUBCOMP | 7 | Client to Server or Server to Client | Publish complete (assured delivery part 3) |
| SUBSCRIBE | 8 | Client to Server | Client subscribe request |
| SUBACK | 9 | Server to Client | Subscribe acknowledgment |
| UNSUBSCRIBE | 10 | Client to Server | Unsubscribe request |
| UNSUBACK | 11 | Server to Client | Unsubscribe acknowledgment |
| PINGREQ | 12 | Client to Server | PING request |
| PINGRESP | 13 | Server to Client | PING response |
| DISCONNECT | 14 | Client to Server | Client is disconnecting |
| Reserved | 15 | Forbidden | Reserved |
标记(Flags)
不同包类型标记位含义不尽相同,具体情况如下表所示:
| Control Packet | Fixed header flags | 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 |
剩余长度(Remaining Length)
Remaining Length 表示的是本数据包剩余部分的字节数,即可变头部和载荷的字节数之和。为了节省传输时的字节数,Remaining Length 采用的是一种变长编码方式。这就是说 Remaining Length 字段的字节数不是固定的,它可能使用1~4个字节。既然 Remaining Length 的字节数是可变的,那么问题来了,我们在解码包数据的时候,怎么知道 Remaining Length 究竟是使用几个字节编码的呢?解决这个问题的办法是,将每个字节的最高位(MSB)作为标志位。若该位的值是1,则意味着下一个字节属于参与 Remaining Length 编码的字节;若该位的值是0,则意味着本字节已经是最后一个参与 Remaining Length 编码的字节了。
举几个
自己动手实现MQTT协议的更多相关文章
- 海鑫智圣:物联网漫谈之MQTT协议
什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...
- 基于MQTT协议进行应用开发
官方协议有句如下的话来形容MQTT的设计思想: "It is designed for connections with remote locations where a "sma ...
- 云巴:基于MQTT协议的实时通信编程模型
概要 有人常问,云巴实时通信系统到底提供了一种怎样的服务,与其他提供推送或 IM 服务的厂商有何本质区别.其实,从技术角度分析,云巴与其它同类厂商都是面向开发者的通信服务,宏观的编程模型都是大同小异, ...
- MQTT协议(一)
MQTT(Message Queue Telemetry Transport),遥测传输协议,提供订阅/发布模式,更为简约.轻量,易于使用,针对受限环境(带宽低.网络延迟高.网络通信不稳定),可以简单 ...
- MQTT协议的简单介绍和服务器的安装
最近公司做的项目中有用到消息推送,经过多方面的筛选之后确定了使用MQTT协议,相对于XMPP,MQTT更加轻量级,并且占用用户很少的带宽. MQTT是IBM推出的一种针对移动终端设备的基于TCP/IP ...
- MQTT协议学习笔记
1.前沿 万物联网的时代即将到来,物联网也由当初的概念开始进一步落实.随着无线网络技术飞速发展,各种设备都可以连接网络,实现远程控制.例如智能家居最近非常火爆,智能插座.智能LED灯.智能摄像头等.在 ...
- 【转载】MQTT学习笔记——MQTT协议体验 Mosquitto安装和使用
http://blog.csdn.net/xukai871105/article/details/39252653 0 前言 MQTT是IBM开发的一个即时通讯协议.MQTT是面向M2M和物联 ...
- 转:XMPP协议、MQTT协议、HTTP协议、CoAP协议的基本比较
一.先看下相关国外的专业数据对四大协议的比较: Protocol CoAP XMP ...
- 物联网MQTT协议分析和开源Mosquitto部署验证
在<物联网核心协议—消息推送技术演进>一文中已向读者介绍了多种消息推送技术的情况,包括HTTP单向通信.Ajax轮询.Websocket.MQTT.CoAP等,其中MQTT协议为IBM制定 ...
随机推荐
- Spring Cloud Alibaba | Nacos集群部署
目录 Spring Cloud Alibaba | Nacos集群部署 1. Nacos支持三种部署模式 2. 集群模式下部署Nacos 2.1 架构图 2.2 下载源码或者安装包 2.3 配置集群配 ...
- Greenplum主备节点切换
1. 场景描述 Greenplum主节点出现故障,需要将standby节点手动切换为master节点,当master节点修复完成后,再将新修复的master节点设置为standyb节点加入到集群中. ...
- 我的it博客开张啦
今天怀着激动地心情,在这里写下第一篇开博.之前也在新浪.网易等申请过博客,并且将新浪博客作为我的个人技术博客,当有一天看到cnblog时,觉得这里的博客以一本精美的书的批复呈现时,顿觉得很有...咋说 ...
- 创建RDD
RDD创建 在Spark中创建RDD的创建方式大概可以分为三种:从集合中创建RDD:从外部存储创建RDD:从其他RDD创建. 由一个已经存在的Scala集合创建,集合并行化,而从集合中创建RDD,Sp ...
- HIVE之 DDL 数据定义 & DML数据操作
DDL数据库定义 创建数据库 1)创建一个数据库,数据库在 HDFS 上的默认存储路径是/user/hive/warehouse/*.db. hive (default)> create dat ...
- http面试笔试常考知识点(一)
1.什么是http HTTP是客户端和服务器端请求和应答的标准.通过使用Web浏览器.网络爬虫或者其它的工具,客户端发起一个到服务器上指定端口(默认端口为80)的HTTP请求.(我们称这个客户端)叫用 ...
- LINUX下查找大文件及大的文件夹
原帖地址:https://www.cnblogs.com/iyoume2008/p/6105590.html 今天正好碰到这样的问题,在博客园中看到有以上地址的一篇文章,照着上面的操作解决了问题,但是 ...
- golang从context源码领悟接口的设计
注:写帖子时go的版本是1.12.7 go语言中实现一个interface不用像其他语言一样需要显示的声明实现接口.go语言只要实现了某interface的方法就可以做类型转换.go语言没有继承的概念 ...
- 快速清理maven仓库中下载错误的文件
有时候使用pom文件下载依赖文件的时候突然网络异常,可能会出现依赖文件出现破损,导致怎么都不能使用,也没有重新下载. 之前解决办法是找到出现破损的文件并删除,让其重新下载,但是这样效率很低,也很难找到 ...
- TreeSet类的排序
TreeSet支持两种排序方法:自然排序和定制排序.TreeSet默认采用自然排序. 1.自然排序 TreeSet会调用集合元素的compareTo(Object obj)方法来比较元素之间大小关系, ...