前言

  • .Net Core 3.1 WebApi
  • 列出了mqtt客户端的封装目的是为了了解运作机制

1、封装mqtt客户端

  • mqtt底层协议基于MQTTnet 版本2.8.5 github地址
  • 实例化【单例注入AOC】
  • 发布消息
  • 订阅消息

实例化

public MqttNetClient(MqttConfig _mqttConfig, EventHandler<MqttApplicationMessageReceivedEventArgs> receivedMessageHanddler,
ICache iCache)
{
mqttConfig = _mqttConfig;
_iCache=iCache;
var factory = new MqttFactory();
mqttClient = factory.CreateMqttClient() as MqttClient;
clientId = "MQTT:Cloud:Client:" + _mqttConfig.ClientIdPre + ":" + Guid.NewGuid();
//实例化一个MqttClientOptionsBulider
options = new MqttClientOptionsBuilder()
.WithTcpServer(_mqttConfig.Server, _mqttConfig.Port)
.WithCredentials(_mqttConfig.Username, _mqttConfig.Password)
.WithClientId(clientId)
.Build();
if (receivedMessageHanddler != null)
{
//是服务器接收到消息时触发的事件,可用来响应特定消息
mqttClient.ApplicationMessageReceived +=receivedMessageHanddler;
}
//是客户端连接成功时触发的事件
mqttClient.Connected+=Connected;
//是客户端断开连接时触发的事件
mqttClient.Disconnected+=Disconnected;
//连接服务器
mqttClient.ConnectAsync(options);
}

发布消息

public async Task PublishAsync(string topic, string content)
{
var message = new MqttApplicationMessageBuilder();
message.WithTopic(topic);
message.WithPayload(content);
message.WithAtMostOnceQoS();
message.WithRetainFlag(false);
await mqttClient.PublishAsync(message.Build());
}

订阅消息

public async Task SubscribeAsync(string topic)
{
await mqttClient.SubscribeAsync(topic);
}

监听mqtt回复消息

随WebApi应用自启动单例MqttClient去消费消息

  • 初始化mqtt连接配置
  • 主动订阅Topic
  • 消费mqtt消息handler

初始化mqtt连接配置&主动订阅Topic代码

var mqttConfig = new MqttConfig
{
Server = _config.Get("EMQServer"),//服务器IP
Port = int.Parse(_config.Get("EMQPort")),//端口
ClientIdPre = "IoT.MQTT"//IoT MQTT连接
}; mqttConfig.TopicList = new List<string>()
{
IoTSubscribeMqttTopicEnum.主动订阅.GetEnumDescription()
};
_mqttNetClient = new MqttNetClient(mqttConfig, ReceivedMessageHandler, _cache);

消费mqtt消息handler

public static void ReceivedMessageHandler(object sender, MqttApplicationMessageReceivedEventArgs e)
{
var topic = e.ApplicationMessage.Topic;
var payload = EncryptUtil.Base64Decode(Encoding.UTF8.GetString(e.ApplicationMessage.Payload)); //如果Topic是主动订阅,则去订阅http接口入参传过来的回复Topic字段
if (topic.Equals(IoTSubscribeMqttTopicEnum.主动订阅.GetEnumDescription()))
{
_mqttNetClient.SubscribeAsync(payload);
}
else
{
var mqttPayloadModel = JsonConvert.DeserializeObject<MqttPayloadModel<MqttContentModel>>(payload);
//插入缓存是为了在回复的时候用,通过MID保持一致
var cacheKey = $"{RedisKeyEnum.MQTT接收消息.GetEnumDescription()}:{mqttPayloadModel.Con.Mid}";
var _cache = _serviceProvider.GetService<ICache>();
_cache.InsertAsync(cacheKey, payload, 5 * 60);
}
}

2、封装http接口

发布消息代码

public async Task<BaseOutModel<PublishMessageOutModel>> PublishMessage(PublishMessageInModel publishInModel)
{
//如果回复Topic不为空,则需要等待mqtt返回消息,通过MID来保持一致
if (!String.IsNullOrEmpty(publishInModel.ReceiveTopic))
{
//先主动订阅
_mqttNetClient.PublishMessageAsync(IoTSubscribeMqttTopicEnum.主动订阅.GetEnumDescription(), EncryptUtil.Base64Encode(publishInModel.ReceiveTopic));
//发布消息
_mqttNetClient.PublishMessageAsync(publishInModel.Topic, publishInModel.Content);
var messageId= JsonConvert.DeserializeObject<MqttPayloadModel<MqttContentModel>>(EncryptUtil.Base64Decode(publishInModel.Content)).Con.Mid;
var cacheKey = $"{RedisKeyEnum.MQTT接收消息.GetEnumDescription()}:{messageId}";
//8秒为超时
var endTime = DateTime.Now.AddSeconds(8);
var received = false; var cacheMsg = String.Empty;
while (!received)
{
cacheMsg = await _cache.GetAsync<String>(cacheKey);
if (cacheMsg != null || DateTime.Now > endTime)
{
received = true;
}
}
//缓存不为空,代表收到了mqtt客户端的响应
if (cacheMsg != null)
{
//移除回复消息缓存
_cache.RemoveAsync(cacheKey);
return new BaseOutModel<PublishMessageOutModel>
{
Message = "发布消息成功,接收消息成功",
Data = new PublishMessageOutModel { Content = cacheMsg, ReceiveStatus = MqttReceiveStatusEnum.Yes.IntToEnum() }
};
}
return new BaseOutModel<PublishMessageOutModel>
{
Message = "发布消息成功,接收消息失败",
Data = new PublishMessageOutModel { ReceiveStatus = MqttReceiveStatusEnum.No.IntToEnum() }
};
}
//如果Topic为空,则只需要发布消息就好
await _mqttNetClient.PublishMessageAsync(publishInModel.Topic, publishInModel.Content);
return new BaseOutModel<PublishMessageOutModel> { Message = "发布消息成功",Data=new PublishMessageOutModel { } };
}

接口请求入参

参数名 必选 类型 说明
Topic string 发布的Topic
Content string 发布的消息体,需要Base64字符串
ReceiveTopic string 接收消息Topic【只有不为空,才会去处理接收设备端mqtt的回复消息】
DeviceSn string 设备编号

接口请求出参

参数名 类型 说明
Data - Content string 回复消息的内容
Data - ReceiveStatus integer 回复状态【1 已回复,0 未回复 即超时】
Code integer 返回状态码【200正常,0有错误】
Message string 错误描述,Code为0使用

接口请求示例

1、在Startup中将HttpClient注入IOC

services.AddHttpClient("iotmqtt", x =>
{
x.BaseAddress= new Uri("http://iotmqtt.liyunzhi.com");
});

2、调用

//请求mqtt的消息体
var content = new
{
Met = "Reboot",
Con = new
{
Mid = Mid,
Ts = Ts,
Sign = Sign,
Cid = Cid
}
}; //请求入参模型
var iotPublishMessageModel = new
{
//请求Topic
Topic = "Device/Reboot",
//请求mqtt的消息体
Content = EncryptUtil.Base64Encode(JsonConvert.SerializeObject(content)),
//消息ID
MessageId = Mid,
//可选参数 需要消息回复则传
ReceiveTopic = Mid+"Device/Reboot",
//设备号
DeviceSn = "123123123"
}; //
var client = _httpClientFactory.CreateClient("iotmqtt");
var httpResponseMessage = client.PostAsJsonAsync("Api/PublishMessage", iotPublishMessageModel).Result;
//如果当前方法用async异步修饰,请不要.Result 请await
//var httpResponseMessage =await client.PostAsJsonAsync("Api/PublishMessage", iotPublishMessageModel);
if (httpResponseMessage.IsSuccessStatusCode)
{
//成功,返回内容自己自行解析,解析参考接口请求出参
var data= httpResponseMessage.Content.ReadAsStringAsync().Result;
//如果当前方法用async异步修饰,请不要.Result 请await
//var data= await httpResponseMessage.Content.ReadAsStringAsync();
}
else
{
//这里是失败的代码了,即发送失败了
}

3、Github开源地址

http接口封装mqtt协议的更多相关文章

  1. Android实现推送方式解决方案 - 长连接+心跳机制(MQTT协议)

    本文介绍在Android中实现推送方式的基础知识及相关解决方案.推送功能在手机开发中应用的场景是越来起来了,不说别的,就我们手机上的新闻客户端就时不j时的推送过来新的消息,很方便的阅读最新的新闻信息. ...

  2. 物联网项目开发必读 深度分析MQTT协议优缺点

    物联网并不仅仅是一种网络,而是一个新的生态环境,它描述的本质是越来越多的使用物品通过网络连接在一起并可使用单个或者多个的终端设备对它们进行各种控制和使用—当然,工业上的物联网通常连接到的石鼓传感器或者 ...

  3. 消息中间件技术 - 浅谈mqtt协议及其实现

    作者:carter(佘虎),转载请注明出处,特别说明:本博文来自博主原博客,为保证新博客中博文的完整性,特复制到此留存,如需转载请注明新博客地址即可. 1.1概念 MQTT(MQ Telemetry ...

  4. MQTT 协议学习:008-在STM32上移植MQTT

    前言 通过前面了解MQTT有关概念.分析了有关的报文,我们对于这个协议也有了更深的认识.但纸上谈来终觉浅,绝知此事要躬行. 本文参考:<STM32+W5500+MQTT+Android实现远程数 ...

  5. Java微信公众平台接口封装源码分享

    前言:      这篇博客是在三月初动手项目的时候准备写的,但是为了完成项目只好拖延时间写这篇博客,顺便也可以在项目中应用我自己总结的的一些经验.今天看来,这些方法的应用还是可以的,至少实现了我之前的 ...

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

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

  7. 转:XMPP协议、MQTT协议、HTTP协议、CoAP协议的基本比较

    一.先看下相关国外的专业数据对四大协议的比较: Protocol                                    CoAP                         XMP ...

  8. Lua编写wireshark插件初探——解析Websocket上的MQTT协议

    一.背景 最近在做物联网流量分析时发现, App在使用MQTT协议时往往通过SSL+WebSocket+MQTT这种方式与服务器通信,在使用SSL中间人截获数据后,Wireshark不能自动解析出MQ ...

  9. Python 基于urllib.request封装http协议类

    基于urllib.request封装http协议类 by:授客QQ:1033553122 测试环境: Python版本:Python 3.3   代码实践 #!/usr/bin/env python ...

随机推荐

  1. Flask 基础组件(七):蓝图

    1 蓝图资源 蓝图有自己的目录,它的所有资源都在其目录下.蓝图的资源目录是由创建Blueprint对象时传入的模块名”__name__”所在的位置决定的.同时,我们可以指定蓝图自己的模板目录和静态目录 ...

  2. 网页排名算法PagaRank

    网页排名算法PageRank PageRank,网页排名,又叫做网页级别.是一种利用网页之间的超链接数据进行计算的方法.它是由Google的两位创始人提出的. 对于用户而言,网页排名一般是比较主观的, ...

  3. HashTable、HashMap与ConCurrentHashMap源码解读

    HashMap 的数据结构 ​ hashMap 初始的数据结构如下图所示,内部维护一个数组,然后数组上维护一个单链表,有个形象的比喻就是想挂钩一样,数组脚标一样的,一个一个的节点往下挂. ​ 我们可以 ...

  4. canvas使用context.drawImage时图片不在画布上展示的问题

    遇到问题:找到图片img元素后,将参数传给context.drawImage(image,10,10)后图片并没有在画布上展示. 解决方案:在外层嵌套document.images[0].onload ...

  5. super,this关键字用法 Java

    super 用法 1.调用父类变量2.调用父类方法3.子类构造方法第一句 this 用法 super关键字用来访问父类内容, this 关键字用来访问本类中的内容, 有三种用法 1.在本类的成员方法中 ...

  6. [jvm] -- 垃圾收集器篇

    垃圾收集器 Serial 收集器 单线程收集器,不仅仅意味着它只会使用一条垃圾收集线程去完成垃圾收集工作,更重要的是它在进行垃圾收集工作的时候必须暂停其他所有的工作线程( "Stop The ...

  7. 关于C#反射(转载)

    反射的用途:     (1)使用Assembly定义和加载程序集,加载在程序集清单中列出模块,以及从此程序集中查找类型并创建该类型的实例.     (2)使用Module了解包含模块的程序集以及模块中 ...

  8. 大型Java进阶专题(九) 设计模式之总结

    前言 ​ 关于设计模式的文章就到这里了,学习这门多设计模式,你是不是有这样的疑惑,发现很多设计模式很类似,经常会混淆某些设计模式.这章节我们将对设计模式做一个总结,看看各类设计模式有什么区别.需要注意 ...

  9. Vue nextTick 学习历程

    nextTick 详解 这是官网的解释,比较简洁精炼,反正我是第一遍什么都没看懂 在下次 DOM 更新循环结束之后执行延迟回调.在修改数据之后立即使用这个方法,获取更新后的 DOM. 经过我一步步测试 ...

  10. laravel报错1071 Specified key was too long; max key length is 1000 bytes

    Laravel 默认使用utf8mb4字符编码,而不是的utf8编码.因此运行php artisan migrate会出现如下错误: [Illuminate\Database\QueryExcepti ...