前言

  • .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. leetcode_1-两数之和_javascript

    题目 1.两数之和 给定一个整数数组 nums 和一个目标值 target,请你在该数组中找出和为目标值的那 两个整数,并返回他们的数组下标. 你可以假设每种输入只会对应一个答案.但是,数组中同一个元 ...

  2. Python Ethical Hacking - NETWORK_SCANNER(1)

    NETWORK_SCANNER Discover all devices on the network. Display their IP address. Display their MAC add ...

  3. Python Ethical Hacking - MAC Address & How to Change(3)

    SIMPLE ALGORITHM Goal  -> Check if MAC address was changed. Steps: 1. Execute and read ifconfig. ...

  4. SpringBoot 接收前端参数的几种方式

    昨天和前端小伙伴在联调是碰到了参数接收不到的错误,我在postman上测试接口是正常的,但是与前端对接时就接受不到参数,请求方式都是get,但是问题就在于json  和 form-data 的区别!这 ...

  5. 深入探究JVM之内存结构及字符串常量池

    前言 Java作为一种平台无关性的语言,其主要依靠于Java虚拟机--JVM,我们写好的代码会被编译成class文件,再由JVM进行加载.解析.执行,而JVM有统一的规范,所以我们不需要像C++那样需 ...

  6. springboot 跨域设置

    /** * Configuration cors */ @Configuration public class MyConfiguration { @Bean public FilterRegistr ...

  7. CentOS7 firewalld docker 端口映射问题,firewall开放端口后,还是不能访问,解决方案

    # 宿主机ip: 192.168.91.19 docker run -itd --name tomcat -p 8080:8080 tomcat /usr/local/apache-tomcat-9. ...

  8. 搞事情?Spring Boot今天一口气发布三个版本

    学无止境?本文已被 https://www.yourbatman.cn 收录,里面一并有Spring技术栈.MyBatis.JVM.中间件等小而美的专栏供以免费学习.关注公众号[BAT的乌托邦]逐个击 ...

  9. LQB2013A05前缀判断

    上一道题,,,把if条件写错了,,,,找了半天的bug我都快哭了, 好了好了 看见这种填空题,先理解题意 然后把代码copy下来,把空格注释掉,然后运行到编译没有错. 再理一下它的思路 // // C ...

  10. 定义 WSGI 接口

    # WSGI服务器调用 def application(environ,start_response): start_response('200 OK',[('Content-Type','text/ ...