20210311 全网唯一,物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现
Posted 微软MVP Eleven
tags:
篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了20210311 全网唯一,物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现相关的知识,希望对你有一定的参考价值。
全网唯一,物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现
一、介绍
MQTT是一种基于TCP/IP协议的应用层协议,它规定了不同应用之间进行数据交换时的传送格式。既然是协议,理论上可以被任何开发语言实现它,以运行在任何平台,这个特性就可以将所有可联网的物品通过此协议的方式进行数据通信,这是其一,之所以被物联网所青睐,还因为它的几个主要的特性:
- 1、使用发布/订阅消息模式,提供一对多的消息发布,解除耦合,各终端之间无关
- 2、对负载内容屏蔽的消息传输,可以对消息订阅者所接受到的内容有所屏蔽
- 3、具体有三种消息发布的服务质量(以后细说)
- 4、小型传输,开销小,固定长度的头部是 2 字节,协议交换最小化,以降低网络流量
- 5、使用Last Will和Testament特性通知有关各方客户端异常中断的机制
二、应用实现
为了更方便的抓包分析,进行了MQTT协议的服务端与客户端的应用实现
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-cpCW0U7s-1616122875658)(assets\\image-20210311120146654.png)]
三、运行机制
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-6NckLvrA-1616122875661)(assets\\image-20210311105921317.png)]
- 服务端:通过云端一个服务器程序开启MQTT服务器(Broker),常见的如EMQ
- 客户端:不管是IoT管理应用程序还是IoT设备,都属性于客户端程序
- 订阅:各客户端程序如果想要接收到别人发送过来的数据,就需要订阅一个主题(Topic)
- 发布:任何客户端都可以根据一个主题向服务器发布消息,服务器会根据订立记录,将消息推送至订阅了对应主题的客户端
四、数据传输格式
MQTT报文大体上包含三大部分:固定报头、可变报头、报文载荷,整体结构如下
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-ygAqcAZK-1616122875662)(assets\\image-20210311112655225.png)]
1、固定报头
第一个字节中高4位保存了消息的类型信息,包含1-14种类型(5.0版本扩充了第15个:认证交换)
- 1 CONNECT – 连接服务端
- 2 CONNACK – 确认连接请求
- 3 PUBLISH – 发布消息
- 4 PUBACK –发布确认
- 5 PUBREC – 发布收到(QoS 2,第一步)
- 6 PUBREL – 发布释放(QoS 2,第二步)
- 7 PUBCOMP – 发布完成(QoS 2,第三步)
- 8 SUBSCRIBE - 订阅主题
- 9 SUBACK – 订阅确认
- 10 UNSUBSCRIBE –取消订阅
- 11 UNSUBACK – 取消订阅确认
- 12 PINGREQ – 心跳请求
- 13 PINGRESP – 心跳响应
- 14 DISCONNECT – 断开连接
- 15 AUTH – 认证交换
2、可变报头
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-Jj69YlLW-1616122875663)(assets\\image-20210311114916413.png)]
3、报文载荷
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-MqOkZdyo-1616122875664)(assets\\image-20210311115816945.png)]
五、报文截获
利用 WireShark进行报文截获,以连接请求报文为例:
[外链图片转存失败,源站可能有防盗链机制,建议将图片保存下来直接上传(img-U1uPmzEA-1616122875665)(assets\\image-20210311120512410.png)]
六、C#代码实现
此处以客户端发送连接请求为例,完整报文拼接实例如下
static void Main(string[] args)
Console.WriteLine("欢迎关注朝夕教育,我是Jovan");
try
string ip = "127.0.0.1";
int port = 1883;
string username = "admin";
string password = "123456";
string clientId = "C001";
//
Socket socketClient = new Socket(AddressFamily.InterNetwork, SocketType.Stream, ProtocolType.Tcp);
socketClient.Connect(ip, port);
Console.WriteLine(">>> TCP 连接通道已建立");
// 建立MQTT连接
// 构建MQTT报文
List<byte> bytes = new List<byte>();
bytes.AddRange(new byte[] 0, 4 );// Protocol Name Length
bytes.AddRange(Encoding.ASCII.GetBytes("MQTT"));// Protocol Name
bytes.Add(4);
byte flag = 0;
flag |= 128;// 用户名标记
flag |= 64; // 密码标记
flag |= 2; // Clean Session 标记
bytes.Add(flag);
// KeepAlive
byte[] bytesKeepAlive = BitConverter.GetBytes((int)TimeSpan.FromSeconds(100).TotalSeconds);
bytes.Add(bytesKeepAlive[1]);
bytes.Add(bytesKeepAlive[0]);
// ClientID
byte[] clienIdBytes = Encoding.ASCII.GetBytes(clientId);
bytes.Add((byte)(clienIdBytes.Length / 256));
bytes.Add((byte)(clienIdBytes.Length % 256));// 长度占两个字节
bytes.AddRange(clienIdBytes);
// UserName
byte[] usernameBytes = Encoding.ASCII.GetBytes(username);
bytes.Add((byte)(usernameBytes.Length / 256));
bytes.Add((byte)(usernameBytes.Length % 256));// 长度占两个字节
bytes.AddRange(usernameBytes);
// Password
byte[] passwordBytes = Encoding.ASCII.GetBytes(password);
bytes.Add((byte)(passwordBytes.Length / 256));
bytes.Add((byte)(passwordBytes.Length % 256));// 长度占两个字节
bytes.AddRange(passwordBytes);
byte[] bufferLen = new byte[] (byte)bytes.Count ;
MemoryStream memoryStream = new MemoryStream();
memoryStream.WriteByte(1 << 4);
memoryStream.Write(bufferLen, 0, (int)bufferLen.Length);
memoryStream.Write(bytes.ToArray(), 0, (int)bytes.Count);
byte[] array = memoryStream.ToArray();
memoryStream.Close();
socketClient.Send(array);
catch (Exception ex)
Console.WriteLine(ex.Message);
Console.ReadLine();
t.Send(array);
catch (Exception ex)
Console.WriteLine(ex.Message);
Console.ReadLine();
以上是关于20210311 全网唯一,物联网MQTT协议报文结构分析以及基于C#代码的报文组装实现的主要内容,如果未能解决你的问题,请参考以下文章
物联网之MQTT3.1.1和MQTT5协议 (15) DISCONNECT报文
物联网之MQTT3.1.1和MQTT5协议 (14) PINGRESP报文
物联网之MQTT3.1.1和MQTT5协议 (12) UNSUBACK 报文
物联网之MQTT3.1.1和MQTT5协议 (13) PINGREQ报文