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" />

在页面中导入:

Dart 复制代码
import 'package:mqtt_client/mqtt_client.dart';

使用案例, 这里我们使用的是wss协议:

Dart 复制代码
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 callback
  void onConnected() {
    print("已连接");
    try {
      // 连接后订阅
      client.subscribe('订阅地址', MqttQos.atLeastOnce);
    } catch (e, s) {
      LoggerHelper.fatal(e, s);
    }
  }

// Disconnected callback
  void 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 callback
  void onSubscribed(String topic) {
    print('订阅成功,主题为: $topic');
  }

// Subscribed failed callback
  void onSubscribeFail(String topic) {
    print('订阅失败,主题为: $topic');
  }

// Unsubscribed callback
  void onUnsubscribed(String? topic) {
    print('Unsubscribed topic: $topic');
  }

// Ping callback
  void pong() {
    print('调用Ping响应客户端回调');
  }
}

创建一个全局的客户端对象

Dart 复制代码
final client = MqttServerClient('请求地址', '');

配置客户端对象

Dart 复制代码
      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;
  • 设置连接消息
Dart 复制代码
 final connMess = MqttConnectMessage()
          .authenticateAs("用户名", "密码")
          .withClientIdentifier('Mqtt_MyClientUniqueId')
          .withWillTopic('willtopic')
          .withWillMessage('My Will message')
          .startClean()
          .withWillQos(MqttQos.atLeastOnce);
 client.connectionMessage = connMess;

连接(异步)

Dart 复制代码
 try {
        print('Connecting');
        await client.connect();
      } catch (e) {
        print('Exception: $e');
        client.disconnect();
      }

监听接受的消息

Dart 复制代码
 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}');
      });

发送消息

Dart 复制代码
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!);
    }
  }

监听连接状态和订阅的回调

Dart 复制代码
// Connected callback
  void onConnected() {
    print("已连接");
    try {
      // 连接后订阅
      client.subscribe('订阅地址', MqttQos.atLeastOnce);
    } catch (e, s) {
      LoggerHelper.fatal(e, s);
    }
  }

// Disconnected callback
  void 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 callback
  void onSubscribed(String topic) {
    print('订阅成功,主题为: $topic');
  }

// Subscribed failed callback
  void onSubscribeFail(String topic) {
    print('订阅失败,主题为: $topic');
  }

// Unsubscribed callback
  void onUnsubscribed(String? topic) {
    print('Unsubscribed topic: $topic');
  }

// Ping callback
  void pong() {
    print('调用Ping响应客户端回调');
  }
相关推荐
学代码的小前端1 分钟前
0基础学前端-----CSS DAY9
前端·css
joan_855 分钟前
layui表格templet图片渲染--模板字符串和字符串拼接
前端·javascript·layui
迷雾漫步者9 分钟前
Flutter组件————PageView
flutter·跨平台·dart
m0_7482361137 分钟前
Calcite Web 项目常见问题解决方案
开发语言·前端·rust
Watermelo6171 小时前
详解js柯里化原理及用法,探究柯里化在Redux Selector 的场景模拟、构建复杂的数据流管道、优化深度嵌套函数中的精妙应用
开发语言·前端·javascript·算法·数据挖掘·数据分析·ecmascript
m0_748248941 小时前
HTML5系列(11)-- Web 无障碍开发指南
前端·html·html5
m0_748235611 小时前
从零开始学前端之HTML(三)
前端·html
一个处女座的程序猿O(∩_∩)O3 小时前
小型 Vue 项目,该不该用 Pinia 、Vuex呢?
前端·javascript·vue.js
hackeroink6 小时前
【2024版】最新推荐好用的XSS漏洞扫描利用工具_xss扫描工具
前端·xss
迷雾漫步者8 小时前
Flutter组件————FloatingActionButton
前端·flutter·dart