Flutter之使用mqtt进行连接和信息传输的使用案例
目录
引言
什么是MQTT?
在Flutter中使用MQTT
安装
iOS
安卓
创建一个全局的客户端对象
配置客户端对象
连接(异步)
监听接受的消息
发送消息
监听连接状态和订阅的回调
引言
随着移动应用开发技术的发展,实时通信成为了许多应用程序不可或缺的一部分。无论是社交应用中的即时消息传递,还是物联网(IoT)设备之间的数据交换,都需要一个高效稳定的通信机制。MQTT(Message Queuing Telemetry Transport)作为一种轻量级的消息协议,非常适合于这种场景。本文将介绍如何在Flutter项目中集成MQTT,并通过一个简单的示例来演示其基本用法。
什么是MQTT?
MQTT是一种基于发布/订阅模式的轻量级消息协议,设计初衷是为了提供低开销、低带宽的网络连接。它特别适合于远程位置的通信,如传感器与中央服务器之间的数据传输。MQTT的主要特点包括:
- 轻量级:非常小的代码占用空间和带宽使用。
- 发布/订阅模型:允许一对多的消息分发,即一个消息可以发送给多个客户端。
- 服务质量(QoS):提供了三种不同的服务质量级别,以满足不同场景下的需求。
- 安全性:支持TLS/SSL加密,确保数据传输的安全性。
在Flutter中使用MQTT
首先需要安装mqtt_client这个依赖,执行下面命令
flutter pub add mqtt_client
安装
如果您在 Android 或 iOS 设备上的 Flutter 环境中使用客户端,则需要进行以下设备权限设置。
iOS
将以下键添加到位于ios/Runner/Info.plist的Info.plist文件中:
<key>NSLocalNetworkUsageDescription</key>
<string>Looking for local tcp Bonjour service</string>
<key>NSBonjourServices</key>
<array><string>mqtt.tcp</string>
</array>
安卓
将以下 Android 权限添加到位于android/app/src/main/AndroidManifest.xml的AndroidManifest.xml文件中:
<uses-permission android:name="android.permission.INTERNET" />
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
在页面中导入:
import 'package:mqtt_client/mqtt_client.dart';
使用案例, 这里我们使用的是wss协议:
import 'dart:convert';
import 'package:flutter_diancan/utils/logger_helper.dart';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';
import 'package:shared_preferences/shared_preferences.dart';class MqttServe {final client = MqttServerClient('请求地址', '');Future<MqttClient> connect() async {try {client.setProtocolV311();client.logging(on: true);client.port = 443; // 端口号client.keepAlivePeriod = 60;client.websocketProtocols = ['mqtt'];client.useWebSocket = true; // 因为我们这里使用的是wss协议所以加这个,这个根据自己的需求来定是否需要client.onConnected = onConnected;client.onDisconnected = onDisconnected;client.onUnsubscribed = onUnsubscribed;client.onSubscribed = onSubscribed;client.onSubscribeFail = onSubscribeFail;client.pongCallback = pong;client.connectTimeoutPeriod = 60;final connMess = MqttConnectMessage().authenticateAs("用户名", "密码").withClientIdentifier('Mqtt_MyClientUniqueId').withWillTopic('willtopic').withWillMessage('My Will message').startClean().withWillQos(MqttQos.atLeastOnce);client.connectionMessage = connMess;try {print('Connecting');await client.connect();} catch (e) {print('Exception: $e');client.disconnect();}client.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) async {final recMessage = c![0].payload as MqttPublishMessage;final payload = MqttPublishPayload.bytesToStringAsString(recMessage.payload.message);print('Received message:$payload from topic: ${c[0].topic}');});} catch (e, s) {LoggerHelper.fatal(e, s);}return client;}Future<void> sendMessage() async {if (client.connectionStatus?.state == MqttConnectionState.connected) {final builder = MqttClientPayloadBuilder();var payloadObject = {'MsgData': "发送成功啦"};print("发送的信息:${json.encode(payloadObject)} ");builder.addUTF8String(json.encode(payloadObject));client.publishMessage('发送消息的订阅地址', MqttQos.atLeastOnce, builder.payload!);}}// Connected callbackvoid onConnected() {print("已连接");try {// 连接后订阅client.subscribe('订阅地址', MqttQos.atLeastOnce);} catch (e, s) {LoggerHelper.fatal(e, s);}}// Disconnected callbackvoid onDisconnected() async {print('已断开');final SharedPreferences prefs = await SharedPreferences.getInstance();String? token = prefs.getString('token');if (token != null) {reconnect();}}Future<void> reconnect() async {print("重连中");int retryCount = 0;const maxRetries = 10;const baseRetryInterval = 2; // 初始重连间隔时间(秒)while (retryCount < maxRetries) {try {print('Reconnecting attempt ${retryCount + 1}...');await client.connect();if (client.connectionStatus?.state == MqttConnectionState.connected) {print('Reconnected successfully.');break;}} catch (e) {print('Reconnect failed: $e');}// 计算下一次重连间隔时间(指数退避)int retryInterval = baseRetryInterval * (2 << retryCount);await Future.delayed(Duration(seconds: retryInterval));retryCount++;}}// 关闭Future<void> close() async {try {// 重新订阅client.unsubscribe('订阅地址');} catch (e, s) {LoggerHelper.fatal(e, s);}client.disconnect();}// Subscribed callbackvoid onSubscribed(String topic) {print('订阅成功,主题为: $topic');}// Subscribed failed callbackvoid onSubscribeFail(String topic) {print('订阅失败,主题为: $topic');}// Unsubscribed callbackvoid onUnsubscribed(String? topic) {print('Unsubscribed topic: $topic');}// Ping callbackvoid pong() {print('调用Ping响应客户端回调');}
}
-
创建一个全局的客户端对象
final client = MqttServerClient('请求地址', '');
-
配置客户端对象
client.setProtocolV311();client.logging(on: true);client.port = 443; // 端口号client.keepAlivePeriod = 60;client.websocketProtocols = ['mqtt'];client.useWebSocket = true; // 因为我们这里使用的是wss协议所以加这个,这个根据自己的需求来定是否需要client.onConnected = onConnected;client.onDisconnected = onDisconnected;client.onUnsubscribed = onUnsubscribed;client.onSubscribed = onSubscribed;client.onSubscribeFail = onSubscribeFail;client.pongCallback = pong;client.connectTimeoutPeriod = 60;
- 设置连接消息
final connMess = MqttConnectMessage().authenticateAs("用户名", "密码").withClientIdentifier('Mqtt_MyClientUniqueId').withWillTopic('willtopic').withWillMessage('My Will message').startClean().withWillQos(MqttQos.atLeastOnce);client.connectionMessage = connMess;
-
连接(异步)
try {print('Connecting');await client.connect();} catch (e) {print('Exception: $e');client.disconnect();}
-
监听接受的消息
client.updates!.listen((List<MqttReceivedMessage<MqttMessage?>>? c) async {final recMessage = c![0].payload as MqttPublishMessage;final payload = MqttPublishPayload.bytesToStringAsString(recMessage.payload.message);print('Received message:$payload from topic: ${c[0].topic}');});
-
发送消息
Future<void> sendMessage() async {if (client.connectionStatus?.state == MqttConnectionState.connected) {final builder = MqttClientPayloadBuilder();var payloadObject = {'MsgData': "发送成功啦"};print("发送的信息:${json.encode(payloadObject)} ");builder.addUTF8String(json.encode(payloadObject));client.publishMessage('发送消息的订阅地址', MqttQos.atLeastOnce, builder.payload!);}}
-
监听连接状态和订阅的回调
// Connected callbackvoid onConnected() {print("已连接");try {// 连接后订阅client.subscribe('订阅地址', MqttQos.atLeastOnce);} catch (e, s) {LoggerHelper.fatal(e, s);}}// Disconnected callbackvoid onDisconnected() async {print('已断开');final SharedPreferences prefs = await SharedPreferences.getInstance();String? token = prefs.getString('token');if (token != null) {reconnect();}}Future<void> reconnect() async {print("重连中");int retryCount = 0;const maxRetries = 10;const baseRetryInterval = 2; // 初始重连间隔时间(秒)while (retryCount < maxRetries) {try {print('Reconnecting attempt ${retryCount + 1}...');await client.connect();if (client.connectionStatus?.state == MqttConnectionState.connected) {print('Reconnected successfully.');break;}} catch (e) {print('Reconnect failed: $e');}// 计算下一次重连间隔时间(指数退避)int retryInterval = baseRetryInterval * (2 << retryCount);await Future.delayed(Duration(seconds: retryInterval));retryCount++;}}// 关闭Future<void> close() async {try {// 重新订阅client.unsubscribe('订阅地址');} catch (e, s) {LoggerHelper.fatal(e, s);}client.disconnect();}// Subscribed callbackvoid onSubscribed(String topic) {print('订阅成功,主题为: $topic');}// Subscribed failed callbackvoid onSubscribeFail(String topic) {print('订阅失败,主题为: $topic');}// Unsubscribed callbackvoid onUnsubscribed(String? topic) {print('Unsubscribed topic: $topic');}// Ping callbackvoid pong() {print('调用Ping响应客户端回调');}
相关文章:
Flutter之使用mqtt进行连接和信息传输的使用案例
目录 引言 什么是MQTT? 在Flutter中使用MQTT 安装 iOS 安卓 创建一个全局的客户端对象 配置客户端对象 连接(异步) 监听接受的消息 发送消息 监听连接状态和订阅的回调 引言 随着移动应用开发技术的发展,实时通信成为…...
汽车HiL测试:利用TS-GNSS模拟器掌握硬件性能的仿真艺术
一、汽车HiL测试的概念 硬件在环(Hardware-in-the-Loop,简称HiL)仿真测试,是模型基于设计(Model-Based Design,简称MBD)验证流程中的一个关键环节。该步骤至关重要,因为它整合了实际…...
【MyBatisPlus·最新教程】包含多个改造案例,常用注解、条件构造器、代码生成、静态工具、类型处理器、分页插件、自动填充字段
文章目录 一、MyBatis-Plus简介二、快速入门1、环境准备2、将mybatis项目改造成mybatis-plus项目(1)引入MybatisPlus依赖,代替MyBatis依赖(2)配置Mapper包扫描路径(3)定义Mapper接口并继承BaseM…...
前端知识点---rest(javascript)
文章目录 前端知识点---rest(javascript)rest的用法基本语法特点使用场景与扩展运算符(spread)区别小练习 前端知识点—rest(javascript) rest出现于ES2015 function doSum(a,b, ...args) //示例中的args就是一个rest参数 //它会将后续的所有参数存储…...
13. 猜最大公约数最小公倍数小游戏
文章目录 概要整体架构流程技术名词解释技术细节小结 1. 概要 ~ Jack Qiao对米粒说:“今天咱们玩个小游戏,这个游戏的玩家需要猜出,两个随机生成的整数的最大公约数(GCD)和最小公倍数(LCM)。如…...
Git 多仓库提交用户信息动态设置
Git 多仓库提交用户信息动态设置 原文地址:dddhl.cn 前言 在日常开发中,我们可能需要同时管理多个远程仓库(如 GitHub、Gitee、GitLab),而每个仓库使用不同的邮箱和用户名。比如,GitHub 和 Gitee 使用相…...
2024.6使用 UMLS 集成的基于 CNN 的文本索引增强医学图像检索
Enhancing Medical Image Retrieval with UMLS-Integrated CNN-Based Text Indexing 问题 医疗图像检索中,图像与相关文本的一致性问题,如患者有病症但影像可能无明显异常,影响图像检索系统准确性。传统的基于文本的医学图像检索࿰…...
了解Redis(第一篇)
目录 Redis基础 什么事Redis Redis为什么这么快 除了 Redis,你还知道其他分布式缓存方案吗? 说-下 Redis 和 Memcached 的区别和共同点 为什么要用Redis? 什么是 Redis Module?有什么用? Redis基础 什么事Redis Redis (REmote DIctionary S…...
UE5 第一人称射击项目学习(二)
在上一章节中。 得到了一个根据视角的位置创建actor的项目。 现在要更近一步,对发射的子弹进行旋转。 不过,现在的子弹是圆球形态的,所以无法分清到底怎么旋转,所以需要把子弹变成不规则图形。 现在点开蓝图。 这里修改一下&…...
npm/cnpm的使用
npm 1、安装npm 前往nodejs官网下载安装node 验证是否安装成功node node -v node安装npm也会安装 npm -v 2、使用npm 1. 初始化项目 在一个项目文件夹中运行: npm init 根据提示输入项目信息(如项目名称、版本号等)。 如果你希望快速初…...
go-zero(六) JWT鉴权
go-zero JWT鉴权 还记得我们之前登录功能,返回的信息是token吗? 这个token其实就是JSON Web Token简称JWT,它是一种开放标准(RFC 7519),用于在网络应用环境间安全地传递声明信息。 它是一种基于 JSON 的令牌…...
做一个FabricJS.cc的中文文档网站——面向markdown编程
📢欢迎点赞 :👍 收藏 ⭐留言 📝 如有错误敬请指正,赐人玫瑰,手留余香!📢本文作者:由webmote 原创📢作者格言:新的征程,用爱发电&#…...
开发 + 安全:网络安全的协作方法
开发团队和安全团队之间由来已久的紧张关系一直是组织内部摩擦的根源。开发人员优先考虑速度和效率,旨在通过快节奏、迭代的开发周期快速交付功能和产品并高效前进。另一方面,安全团队努力平衡风险和创新,但必须专注于使用护栏保护敏感数据和…...
Next.js- App Router 概览
#题引:我认为跟着官方文档学习不会走歪路 一:App Router与Page Router 在 v13 版本中,Next.js 引入了一个基于 React 服务器组件 构建的新的 App Router,而在这之前,Next.js 使用的是Page Router。 目录结构 pages …...
python oa服务器巡检报告脚本的重构和修改(适应数盾OTP)有空再去改
Two-Step Vertification required: Please enter the mobile app OTPverification code: 01.因为巡检的服务器要双因子认证登录,也就是登录堡垒机时还要输入验证码。这对我的巡检查服务器的工作带来了不便。它的机制是每一次登录,算一次会话…...
【工控】线扫相机小结 第四篇
背景 这一片主要是对第三篇继续补充。话说上一篇讲到了两种模式的切换,上一篇还遗留了一个Bug,在这一篇里进行订正! 代码回顾 /// <summary>/// 其实就是打开触发/// </summary>void SetLineSacanWorkMode(){-----首先设置为帧…...
亲测解决Unpack operator in subscript requires Python 3.11 or newer
这个问题是在小虎想提前定义一个list,然后作为index list来调用另一个list里面的变量出现的问题。 环境 Ubuntu 22.04 + python 3.10 故障代码示例 NoneList = [None] * opt.spatial_dims TargetMask = Target[i] == torch.arange(1...
数据结构 ——— 堆排序算法的实现
目录 前言 向下调整算法(默认建大堆) 堆排序算法的实现(默认升序) 前言 在之前几章学习了如何用向上调整算法和向下调整算法对数组进行建大/小堆数据结构 ——— 向上/向下调整算法将数组调整为升/降序_对数组进行降序排序代码…...
On-Chip-Network之Topology
片上网络拓扑决定了网络中节点和通道之间的物理布局和连接。拓扑对整体网络性价比的影响是巨大的。拓扑决定了消息 必须经过的跳数(或路由器)以及跳数之间的互连长度,从而显著影响网络延迟。由于经过路由器和链路会产生功耗,因此 …...
2024年11月21日Github流行趋势
项目名称:twenty 项目维护者:charlesBochet, lucasbordeau, Weiko, FelixMalfait, bosiraphael项目介绍:正在构建一个由社区支持的现代化Salesforce替代品。项目star数:21,798项目fork数:2,347 项目名称:p…...
告别道路预测老套路:用ParkPredict+模型思路,解决停车场里的‘鬼探头’难题
破解泊车场景预测困局:ParkPredict模型的技术革新与实践停车场里的每一次转向、倒车和避让,都是对自动驾驶系统预测能力的极限挑战。与开放道路的规则明确不同,这里没有清晰的车道线指引,没有统一的行驶方向,只有随时可…...
告别FTP龟速:用NTFS-3G在CentOS7上直连移动硬盘拷贝200G大文件
告别FTP龟速:用NTFS-3G在CentOS7上直连移动硬盘拷贝200G大文件当面对数百GB的设计素材、日志文件或数据库备份需要迁移时,传统的FTP传输往往会成为效率瓶颈。我曾在一个视频处理项目中,需要将230GB的4K原始素材从移动硬盘导入服务器ÿ…...
上线前最后一道防线,DeepSeek代码审查如何帮你拦截87%的CVE类缺陷?
更多请点击: https://intelliparadigm.com 第一章:上线前最后一道防线,DeepSeek代码审查如何帮你拦截87%的CVE类缺陷? 在软件交付生命周期末期,传统人工代码审计与通用SAST工具常因误报率高、上下文理解弱而漏检高危漏…...
Owl-Alpha 新手快速上手指南
在处理大规模数据或构建高性能应用时,我们常常会遇到一个棘手的问题:如何在不阻塞主线程的情况下,高效地执行耗时任务?无论是处理图像、解析大型文件,还是进行复杂的数学运算,传统的单线程模式往往会让界面…...
Python UiAutomation实战:从网页数据抓取到桌面应用,一个库打通数据采集全链路
Python UiAutomation实战:打通数据采集全链路的智能解决方案 在数据驱动的商业环境中,企业常常面临跨平台数据采集的挑战——财务系统里的交易记录需要与网站后台的报表进行交叉分析,销售数据要从桌面软件导出后上传到云端处理系统。传统的人…...
2026 文章代码高亮方案选型
将基于 Prism.js 或 Highlight.js 的传统高亮方案与基于 Shiki 的现代化高亮方案进行对比,其核心区别在于底层解析原理的不同(正则表达式 vs. TextMate 语法树)。 以下是两种方案的底层原理、各自优缺点、核心对比矩阵以及适用场景的详细分析…...
差分隐私GDP机制紧密度量化:从隐私剖面到∆度量的实践指南
1. 差分隐私GDP机制:从理论到实践,如何量化隐私保护紧密度在差分隐私(Differential Privacy, DP)的实际部署中,尤其是在机器学习的隐私保护训练(如DP-SGD)场景里,我们常常面临一个核…...
uWSGI目录穿越漏洞CVE-2018-7490深度利用与防御实战
1. 这不是“读文件”那么简单:uWSGI目录穿越在真实攻防链中的定位与误判代价你刚在Vulfocus靶场里跑通了CVE-2018-7490的PoC,用curl "http://target:8080/?p../../../../etc/passwd"成功读出了root:x:0:0:root:/root:/bin/bash,截…...
危急时刻的六条基本安全提示
人机协作,AI模型:Deepseek 仅供参考 危急时刻的六条基本安全提示 以下内容仅为通用性安全建议,供在紧急情况下保持冷静、保护自身安全时参考。所有建议均基于常理和公共安全常识,不包含任何具体操作细节或可能被不当使用的信息…...
万星easy-vibe:描述需求即发布 零基础无需学语法
开源Easy-Vibe是一套开源AI编程学习方案,把学习顺序从先学语法再做项目翻转为直接做项目。文章拆解了项目驱动、提示词编写、AI编辑器和多Agent协作的完整流程,解释了为什么想法比语法更重要。 github上datawhalechina/easy-vibe:它在GitHub…...
