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响应客户端回调');
  }
相关推荐
卸任23 分钟前
Vibe coding工具使用心得
前端·ai编程
wuxianda10301 小时前
2026 苹果审核 4.3 (a) 被拒全解析|Guideline 4.3a spam 马甲包拒审原因 + 避坑方案
flutter·uni-app·苹果上架
brien91 小时前
为什么模型标称 1M,Google Antigravity 聊到 256K 却突然失忆?我们给它做了个原生行内监控圆环
前端
修炼的dance1 小时前
录屏关键帧间隔多少合适?六档实测,2秒到10秒小了75.6%
前端
Lank_M1 小时前
关键帧从1秒拉到10秒,录屏小87.3%,摄像头小10.8%
前端
补码缩写2 小时前
用 ffprobe 拆录屏,八成字节在 29 个关键帧上
前端
百思不得小李2 小时前
一个复杂的动态表单是如何实现的
前端·设计模式
陈随易2 小时前
Cmder 停更 17 个月,2.0 支持 Windows Terminal
前端·后端·程序员
风骏时光牛马2 小时前
前沿视界
前端
kyriewen2 小时前
我扫了 814 个依赖,97 个还敢要 Node 10
前端·javascript·程序员