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();
}


  • 1
    点赞
  • 6
    收藏
    觉得还不错? 一键收藏
  • 2
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值