• .Net Core下使用MQTT协议直连IoT平台


    【摘要】 .Net平台通过原生MQTT接口,作为南向设备对接OceanConnect平台

    因为种种历史原因吧,目前华为平台上对.net的支持案例SDK确实比较少,当看到各种语言的SDK和Demo,唯独缺.net平台的,广大.net开发者也会怀疑.net是不是真的不适合IoT,甚至是互联网。

    我的分析是

    1、微软从.Net Framework开始所谓的跨平台,仅仅局限在Windows个版本的平台,对其他系统并不支持

    2、微软多年以来给人的感觉是闭源,和Linux是死对头,在华为2019HC大会上和一位鲲鹏的工程师聊天也佐证了这一点,他们2019年了还依然对微软是这种认识

    一提到开源,一提到互联网、分布式,.net就被轻视,这种态度很常见。

    但是,我说的是但是,广大的.neter为什么选择.net平台的,为何mono这么多年都对.net跨平台不懈努力?为什么visual studio被称为宇宙第一的IDE?

    那就是生产力。

    以前的种种都成为历史了,自从.net core被提出,微软已经是一个亲Linux的代表了,跨平台、高性能已经是核心基础 。且不提Windows IoT, .net core在Linux,Arm上已经非常成熟了,树莓派们的小设备都能轻松玩转。

    作为平台也好,工具也好就是为了提高生产力的。再来看看.net对接华为IoT平台,我现在从之前羡慕C/C++,Java,PHP等,在华为的帮助文档中都提供了南向设备的SDK,北向接口的SDK。我也一度怀疑华为为何不提供.net的SDK?没有SDK的帮助,对接将是比其他语言更要复杂,困难要一点点的肯。我也尝试在.net里用[DllImport]特性用c的sdk。

    到现在,其实我可以负责任的告诉大家,.net根本不需要华为提供SDK,SDK本身对接口封装后就有不少限制,.net就利用公开的原始接口开发,非常简单和便捷,而且应用起来也灵活。这就是.net的强大生产力,而且.net framework(也可对接桌面程序)和.net core都可以实现。

    1、北向接口对接

    因为工作关系,对北向接口比较熟悉,华为提供的是restful的接口,和语言无关,实现起来轻车熟路。当时遇到一个困难就是用错了证书密码,现在回头看帮助文档中也说明了用哪个证书和密码,我也专门写过一个帖子来说明北向接口对接。

    2、南向设备对接

    南向设备比较复杂,对我来说确实是陌生的领域,小熊派这种MCU的板子恐怕只能有C来搞了,借助LiteOS+IoT Studio 也可以胜任。在我的计划中类似小熊派这样的设备甚至是定制更轻巧的,是物联网的常用终端类型。但还有些场景需要配合更丰富的应用,C恐怕做不到(起码需要带个界面啥的我做不到),或者说做到成本太高,在类似树莓派的独立设备,甚至是在Windows电脑上外接其他设备的场景,.net正是用武之地。

    .Net core + Mqttnet 作为南向设备对接华为IoT平台

    开发工具:Visual Studio 2019,.Net core 2.2和.net framework 4.7.2

    Nuget包:MQTTnet 版本v3.0.8

    核心内容三个事件

    连接Mqtt服务器:

    mqttClient.ConnectAsync(options);

    订阅服务器Topic

    var result = await mqttClient.SubscribeAsync(new TopicFilter()
    {
        Topic = topicSubscribe,
        QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce
    
    });

    服务器发送命令的响应

    mqttClient.UseApplicationMessageReceivedHandler(async handle =>
    {
        var payload = Encoding.Default.GetString(handle.ApplicationMessage.Payload);
        var data = JsonConvert.DeserializeObject<ReceivedMessage>(payload);
        Console.WriteLine($"收到MSG:{payload}");
        var result = await mqttClient.PublishAsync(topicPublish, $"{{"msgType":"deviceRsp","mid":{data.Mid},"errcode":0,"body":{{"response":"ok"}}}}");
        Console.WriteLine($"设备响应命令:{result.ReasonCode}");
    });

    上报设备多信息到服务器:

    var msg = new SendMessage
    {
    	Data = new List<Service>
    	{
    		 new Service
    		 {
    			 ServiceData = new Dictionary<string, dynamic> { { "storage", st }, { "usedPercent", 10 }  },
    			 ServiceId = "Storage",
    			 EventTime = DateTime.UtcNow.ToString("yyyyMMddTHHmmssZ")
    		 }
    	}
    };
    var payload = JsonConvert.SerializeObject(msg, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
    var result = await mqttClient.PublishAsync(topicPublish, payload);

    完整代码如下:

    using MQTTnet;
    using MQTTnet.Client;
    using MQTTnet.Client.Connecting;
    using MQTTnet.Client.Options;
    using System;
    using System.Collections.Generic;
    using System.Runtime.InteropServices;
    using System.Threading.Tasks;
    using System.Linq;
    using MQTTnet.Client.Receiving;
    using System.Text;
    using System.Security.Cryptography.X509Certificates;
    using MQTTnet.Client.Subscribing;
    using MQTTnet.Extensions.ManagedClient;
    using System.Security.Cryptography;
    using Newtonsoft.Json;
    using Newtonsoft.Json.Serialization;
    
    namespace CampusApp
    {
        class Program
        {
            private static IMqttClient mqttClient;
                        
            private static string topicPublish = "/huawei/v1/devices/00b1db5e-5331-4ade-8b62-6c670e6c8d98/data/json";
            private static string topicSubscribe = "/huawei/v1/devices/00b1db5e-5331-4ade-8b62-6c670e6c8d98/command/json";
            private static async Task ConnectMqttServerAsync()
            {
                if (mqttClient == null)
                {
                    mqttClient = new MqttFactory().CreateMqttClient();
    
                    mqttClient.UseConnectedHandler(async handle =>
                    {
                       
                            var result = await mqttClient.SubscribeAsync(new TopicFilter()
                            {
                                Topic = topicSubscribe,
                                QualityOfServiceLevel = MQTTnet.Protocol.MqttQualityOfServiceLevel.AtLeastOnce
    
                            });
                            Console.WriteLine("连接成功");
                      
                    });
                    mqttClient.UseApplicationMessageReceivedHandler(async handle =>
                    {
                        
                            var payload = Encoding.Default.GetString(handle.ApplicationMessage.Payload);
                            var data = JsonConvert.DeserializeObject<ReceivedMessage>(payload);
                            Console.WriteLine($"收到MSG:{payload}");
                            var result = await mqttClient.PublishAsync(topicPublish, $"{{"msgType":"deviceRsp","mid":{data.Mid},"errcode":0,"body":{{"response":"ok"}}}}");
                            Console.WriteLine($"设备响应命令:{result.ReasonCode}");
                      
    
                    });
                    mqttClient.UseDisconnectedHandler(handle =>
                    {
                        Console.WriteLine($"MQTT 断开连接");
                    });
                }
               
    			var utctime =  DateTime.UtcNow.ToString("yyyyMMddhh");
    			var pwd = Encrypt("9e4fbd64f6be2337732a", utctime);
    			var options = new MqttClientOptionsBuilder()
    				.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311)
    				.WithClientId($"00b1db5e-5331-4ade-8b62-6c670e6c8d98_0_0_{utctime}")
    				.WithTcpServer("49.4.93.24", 8883)
    				.WithCredentials("00b1db5e-5331-4ade-8b62-6c670e6c8d98", pwd)
    				//.WithKeepAlivePeriod(TimeSpan.FromSeconds(10))
    				.WithKeepAliveSendInterval(TimeSpan.FromSeconds(3))
    				.WithTls(new MqttClientOptionsBuilderTlsParameters()
    				{
    					AllowUntrustedCertificates = false,
    					UseTls = true,
    					Certificates = new List<byte[]> { new X509Certificate2("rootcert.pem").Export(X509ContentType.Cert) },
    					CertificateValidationCallback = delegate { return true; },
    					IgnoreCertificateChainErrors = false,
    					IgnoreCertificateRevocatireplaceStrings = false
    				})
    				.WithCleanSession()
    				.Build();
    			try
                {
                    var result = await mqttClient.ConnectAsync(options);
                    Console.WriteLine(result.ResultCode);
                }
                catch (Exception ex)
                {
    
                    Console.WriteLine(ex.Message);
                }
            }
            static string Encrypt(string message, string secret)
            {
                secret = secret ?? "";
                //var encoding = new System.Text.ASCIIEncoding();
                var encoding = new System.Text.UTF8Encoding();
                byte[] keyByte = encoding.GetBytes(secret);
                byte[] messageBytes = encoding.GetBytes(message);
                using (var hmacsha256 = new HMACSHA256(keyByte))
                {
                    byte[] hashmessage = hmacsha256.ComputeHash(messageBytes);
                    //return Convert.ToBase64String(hashmessage);
                    StringBuilder rst = new StringBuilder();
                    for (int i = 0; i < hashmessage.Length; i++)
                    {
                        rst.Append(hashmessage[i].ToString("x2"));
                    }
                    return rst.ToString();
                }
            }
            static async Task Main(string[] args)
            {
                try
                {
                    _ = Task.Run(async () =>
                    {
                        await ConnectMqttServerAsync();
                    });
    
                    while (mqttClient?.IsConnected != true)
                    {
                        await Task.Delay(1000);
                    }
    
                    while (true)
                    {
                        Console.WriteLine("输入数字 storage:");
                        var input = Console.ReadLine();
                        if (!int.TryParse(input, out int st))
                            continue;
    
                        var msg = new SendMessage
                        {
                            Data = new List<Service>
                            {
                                 new Service
                                 {
                                     ServiceData = new Dictionary<string, dynamic> { { "storage", st }, { "usedPercent", 10 }  },
                                     ServiceId = "Storage",
                                     EventTime = DateTime.UtcNow.ToString("yyyyMMddTHHmmssZ")
                                 }
                            }
                        };
                        var payload = JsonConvert.SerializeObject(msg, new JsonSerializerSettings { ContractResolver = new CamelCasePropertyNamesContractResolver() });
                        var result = await mqttClient.PublishAsync(topicPublish, payload);
                        Console.WriteLine($"Publish Result: {result.ReasonCode}");
                    }
                }
                catch (Exception ex)
                {
                    Console.WriteLine(ex.Message + ex.StackTrace);
                }
            }
        }
    }

    看这段代码多买的简单,就搞定了整个过程。

    我在开发中重新整理了一下,作为一个SDK提供给.net framework和.net core两边使用,封装之后更加简单。完成之后作为nuget包再分享。 

    作者:神龙居市

  • 相关阅读:
    宠物的生长(多态)
    程序员和程序狗
    表彰优秀学生(多态)
    [leetcode] Anagrams
    [leetcode] Add Two Numbers
    [leetcode] Add Binary
    [leetcode] 4Sum
    [leetcode] 3Sum Closest
    [leetcode] 3Sum
    函数成员修饰之私有方式
  • 原文地址:https://www.cnblogs.com/2020-zhy-jzoj/p/13165295.html
Copyright © 2020-2023  润新知