【摘要】 .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包再分享。

作者:神龙居市

.Net Core下使用MQTT协议直连IoT平台的更多相关文章

  1. [Micropython][ESP8266] TPYBoard V202 之MQTT协议接入OneNET云平台

    随着移动互联网的发展,MQTT由于开放源代码,耗电量小等特点,将会在移动消息推送领域会有更多的贡献,在物联网领域,传感器与服务器的通信,信息的收集,MQTT都可以作为考虑的方案之一.在未来MQTT会进 ...

  2. MQTT协议笔记之mqtt.io项目HTTP协议支持

    前言 MQTT协议诞生之初,就未曾考虑通过HTTP传输.这也正常,网络受限.不稳定网络不太适合HTTP(2G/3G网络大家使用WAP不也OK嘛).在网络较为充裕的桌面端而言,虽纯文本对比二进制而言没多 ...

  3. 海鑫智圣:物联网漫谈之MQTT协议

    什么是MQTT协议 MQTT(消息队列遥测传输协议)是IBM在1999年专门针对物联网等应用场景来制订的轻量级双向消息传输协议,它主要是为了解决物联网上使用到的设备的互相通信的问题,以及这些设备与后端 ...

  4. 基于MQTT协议进行应用开发

    官方协议有句如下的话来形容MQTT的设计思想: "It is designed for connections with remote locations where a "sma ...

  5. 云巴:基于MQTT协议的实时通信编程模型

    概要 有人常问,云巴实时通信系统到底提供了一种怎样的服务,与其他提供推送或 IM 服务的厂商有何本质区别.其实,从技术角度分析,云巴与其它同类厂商都是面向开发者的通信服务,宏观的编程模型都是大同小异, ...

  6. .NET Core下使用gRpc公开服务(SSL/TLS)

    一.前言 前一阵子关于.NET的各大公众号都发表了关于gRpc的消息,而随之而来的就是一波关于.NET Core下如何使用的教程,但是在这众多的教程中基本都是泛泛而谈,难以实际在实际环境中使用,而该篇 ...

  7. MQTT协议(一)

    MQTT(Message Queue Telemetry Transport),遥测传输协议,提供订阅/发布模式,更为简约.轻量,易于使用,针对受限环境(带宽低.网络延迟高.网络通信不稳定),可以简单 ...

  8. MQTT协议的简单介绍和服务器的安装

    最近公司做的项目中有用到消息推送,经过多方面的筛选之后确定了使用MQTT协议,相对于XMPP,MQTT更加轻量级,并且占用用户很少的带宽. MQTT是IBM推出的一种针对移动终端设备的基于TCP/IP ...

  9. MQTT协议学习笔记

    1.前沿 万物联网的时代即将到来,物联网也由当初的概念开始进一步落实.随着无线网络技术飞速发展,各种设备都可以连接网络,实现远程控制.例如智能家居最近非常火爆,智能插座.智能LED灯.智能摄像头等.在 ...

随机推荐

  1. js 重写a标签的href属性和onclick事件

    适应场景:假如移动端拨打电话,需要给a标签添加href属性,但是由于需求,需要链接跳转的同时给a标签添加onclick事件,如果不做任何处理的话,默认执行点击事件,而不会跳转href属性的链接. 怎么 ...

  2. 开发板,pc,虚拟机三者如何互相ping通

    1 安装虚拟机时,主机和虚拟机必须是桥接网卡,保证了ip 同一:192,168,1,xx 2 打开虚拟机之前,先把pc机的无线网卡禁用掉只能使用本地连接,pc通过网线连接上网,打开虚拟机,命令行输入: ...

  3. 关闭redis持久化功能

    关闭redis持久化功能持久化会报如下信息 会影响硬盘写入性能 所以没什么用 就关掉吧 修改redis配置文件,redis.conf 第115行左右. 1.注释掉原来的持久化规则 <pre> ...

  4. fcgi-2.4.1.tar.gz 和 spawn-fcgi-1.6.4.tar.gz 百度云

    链接:https://pan.baidu.com/s/1nEzOkFC0-rfVMDy_BygLWg 提取码:ty0e 美好的东西都是免费滴

  5. 03-MyBatis拦截器机制

    目录 MyBatis拦截器介绍 拦截器的使用 拦截器介绍及配置 源码分析 总结 本文转载自MyBatis拦截器原理探究 MyBatis拦截器介绍 MyBatis提供了一种插件(plugin)的功能,虽 ...

  6. SqlServer2005 查询 第七讲 order by

    今天我们来讲sql命令中的参数order by的用法 order by order by:可以理解成[以某个字段排序] order by a,b // a和b都按升序 order by a,b des ...

  7. PHP Laravel-包含你自己的帮助函数

    你可能想创建一个在应用的任何地方都可以访问的函数,这个教程将帮你实现

  8. Django 通过 ORM 实现表的CRUD

    Django 通过 ORM 实现表的CRUD 单表的创建 修改 setting.py 文件 DATABASES = { 'default': { 'ENGINE': 'django.db.backen ...

  9. 成员函数指针,动态绑定(vc平台)

    上一篇介绍了gcc对成员函数指针做了thunk的处理,本篇介绍vc对成员函数指针如何处理,还有动态绑定相关的处理. 同样用回上一篇的例子: struct point {float x,y;}; str ...

  10. .NET Core 3.0中用 Code-First 方式创建 gRPC 服务与客户端

    .NET Core love gRPC 千呼万唤的 .NET Core 3.0 终于在 9 月份正式发布,在它的众多新特性中,除了性能得到了大大提高,比较受关注的应该是 ASP.NET Core 3. ...