Uygulamalı Eğitimler

Flutter ile MQTT ve mqtt_client: Mobil, Masaüstü ve Web

Flutter uygulamanızı mqtt_client ile MQTT broker'a bağlayın: server ve browser istemcisi, 8883'te TLS, Flutter web için WSS, yeniden bağlanma ve testler.

Güncellendi · 9 dk okuma

Flutter ile MQTT ve mqtt_client: Mobil, Masaüstü ve Web

Flutter ile MQTT kullanmak için mqtt_client paketini ekleyin; Android, iOS ve masaüstü için bir MqttServerClient, Flutter web için bir MqttBrowserClient oluşturun, bağlanın, abone olun ve updates stream'ini dinleyin. En çok kafa karıştıran nokta, iki istemcinin farklı taşıma katmanları kullanmasıdır: server istemcisi TCP veya TLS konuşur, browser istemcisi ise yalnızca WebSocket kullanabilir.

Bu rehberde Flutter için küçük, yeniden kullanılabilir bir MQTT servisi kuruyor, broker.connectmq.com üzerinde TCP, TLS ve güvenli WebSocket yapılandırmalarını gösteriyor ve tüm akışı tarayıcıdan hızlıca test etmenin yolunu anlatıyoruz.

mqtt_client kurulumu

mqtt_client saf bir Dart paketidir; bu yüzden native plugin gerektirmeden tüm Flutter platformlarında çalışır:

Bağımlılığı ekleyin
flutter pub add mqtt_client

Paket, iki ayrı kütüphanede iki istemci sınıfı içerir. Hangisini import ettiğiniz, hangi taşıma katmanını kullanacağınızı belirler:

SınıfImportPlatformlarTaşıma
MqttServerClientpackage:mqtt_client/mqtt_server_client.dartAndroid, iOS, macOS, Windows, LinuxTCP, TLS, WebSocket, güvenli WebSocket
MqttBrowserClientpackage:mqtt_client/mqtt_browser_client.dartFlutter webYalnızca WebSocket ve güvenli WebSocket

mqtt_server_client.dart, tarayıcıda bulunmayan dart:io'yu import eder; bu nedenle web build'de import edildiğinde derleme hata verir. Bunu aşağıda koşullu import ile çözeceğiz. Uygulama yerine saf Dart ile bir komut satırı aracı yazıyorsanız aynı API geçerlidir: Dart ile MQTT yazısına bakın.

TCP üzerinden ilk bağlantı

En basit kurulumla başlayın: 1883 portunda şifresiz TCP. Her istemcinin benzersiz bir client ID'ye ihtiyacı vardır; iki cihaz aynı ID'yi paylaşırsa, biri her bağlandığında broker eski oturumu düşürür. Format seçmek için client ID oluşturucu işinize yarar.

lib/mqtt_demo.dart
import 'dart:math';
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_server_client.dart';

Future<MqttServerClient> connectMqtt() async {
  final clientId = 'flutter-${Random().nextInt(0xFFFFFF).toRadixString(16)}';
  final client = MqttServerClient.withPort('broker.connectmq.com', clientId, 1883);

  client.logging(on: false);
  client.setProtocolV311();
  client.keepAlivePeriod = 30;
  client.connectTimeoutPeriod = 5000; // milisaniye
  client.onConnected = () => print('bağlandı');
  client.onDisconnected = () => print('bağlantı kesildi');

  client.connectionMessage = MqttConnectMessage()
      .withClientIdentifier(clientId)
      .startClean()
      .withWillTopic('testmqtt/flutter-demo/$clientId/status')
      .withWillMessage('offline')
      .withWillQos(MqttQos.atLeastOnce);

  try {
    await client.connect(); // veya client.connect('username', 'password')
  } on Exception catch (e) {
    print('bağlantı başarısız: $e');
    client.disconnect();
    rethrow;
  }

  final state = client.connectionStatus?.state;
  if (state != MqttConnectionState.connected) {
    throw Exception('broker bağlantıyı reddetti: ${client.connectionStatus}');
  }
  return client;
}

connect(), broker CONNECT'i reddettiğinde (yanlış kimlik bilgileri, engellenmiş client ID) NoConnectionException, host veya porta ulaşılamadığında ise SocketException fırlatır. client.connectionStatus çıktısı size dönüş kodunu verir; bu kodu MQTT reason code tablosunda arayabilirsiniz.

Abone olma ve mesaj alma

Gelen mesajlar bir Dart stream'i olan client.updates üzerinden gelir. Önce abone olun, sonra bir kez dinleyin; her abonelik için ayrı ayrı değil:

Abone olun ve dinleyin
const topic = 'testmqtt/flutter-demo/sensors/#';

void listen(MqttServerClient client) {
  client.subscribe(topic, MqttQos.atLeastOnce);

  client.updates!.listen((messages) {
    for (final m in messages) {
      final publish = m.payload as MqttPublishMessage;
      final text = MqttPublishPayload.bytesToStringAsString(publish.payload.message);
      print('${m.topic}: $text');
    }
  });
}

bytesToStringAsString byte'ları metin olarak çözer. JSON için sonucu jsonDecode'a verin; binary payload'lar (CBOR, Protobuf) için doğrudan tipli bir byte buffer olan publish.payload.message'ı kullanın.

Bir widget içinde listen'in döndürdüğü StreamSubscription'ı saklayın ve dispose() içinde iptal edin ya da stream'i state management çözümünüz (Provider, Riverpod, Bloc) üzerinden sunun. Böylece her widget kendi bağlantısını açmak yerine tek bir doğruluk kaynağından yeniden çizilir.

Publish

Payload'lar MqttClientPayloadBuilder ile oluşturulur:

JSON ölçüm değeri yayınlama
import 'dart:convert';

void publishReading(MqttServerClient client, double celsius) {
  final builder = MqttClientPayloadBuilder()
    ..addUTF8String(jsonEncode({'celsius': celsius, 'ts': DateTime.now().toIso8601String()}));

  client.publishMessage(
    'testmqtt/flutter-demo/sensors/kitchen',
    MqttQos.atLeastOnce,
    builder.payload!,
    retain: false,
  );
}

Metin ASCII dışı karakter içerebiliyorsa addString yerine addUTF8String kullanın; addString her UTF-16 kod birimi için tek byte yazar ve ç, ğ, ş gibi harfleri ya da emojileri bozabilir.

8883 portunda TLS

Production için server istemcisini TLS'e geçirin. broker.connectmq.com gibi genel bir CA'dan sertifikası olan broker'lar işletim sisteminin güven deposuyla doğrulanır; ek dosya gerekmez:

TLS yapılandırması
import 'dart:io';

final client = MqttServerClient.withPort('broker.connectmq.com', clientId, 8883)
  ..secure = true
  ..securityContext = SecurityContext.defaultContext;

await client.connect('username', 'password');

Self-signed sertifikalı veya özel CA kullanan bir broker için SecurityContext(withTrustedRoots: false) oluşturun ve asset'lerinizden yüklediğiniz CA sertifikasıyla setTrustedCertificatesBytes() çağırın. onBadCertificate: (_) => true ifadesini hızlı bir yerel deneme dışında kullanmayın: her sertifikayı kabul eder ve TLS'i sessizce etkisiz kılar. Handshake başarısız olursa, terminalden TLS bağlantısını test etmek sorunun broker'da mı yoksa uygulamada mı olduğunu gösterir.

Flutter web: WSS üzerinden MqttBrowserClient

Tarayıcıda MQTT, WebSocket üzerinden çalışır. HTTPS ile sunulan sayfalar yalnızca wss:// bağlantısı açabilir; bu yüzden güvenli WebSocket portunu kullanın ve path'i ekleyin:

lib/mqtt_browser.dart
import 'package:mqtt_client/mqtt_client.dart';
import 'package:mqtt_client/mqtt_browser_client.dart';

MqttClient createClient(String clientId) {
  final client = MqttBrowserClient('wss://broker.connectmq.com/mqtt', clientId)
    ..port = 8084
    ..keepAlivePeriod = 30
    ..websocketProtocols = MqttClientConstants.protocolsSingleDefault;
  return client;
}

Portun URL içinde değil, property olarak ayarlandığına dikkat edin. websocketProtocols satırı, çoğu broker'ın beklediği mqtt subprotocol başlığını gönderir. Yanlış path genellikle upgrade'den hemen sonra WebSocket'in kapanmasıyla kendini gösterir; handshake'in ayrıntıları WebSocket üzerinden MQTT yazısında.

Tüm platformlar için tek kod tabanı

Bir MqttServerClient döndüren (örneğin 8883'te TLS ile) karşılık gelen bir lib/mqtt_server.dart oluşturun ve doğru dosyayı derleme zamanında seçin:

lib/mqtt_factory.dart
export 'mqtt_server.dart'
    if (dart.library.js_interop) 'mqtt_browser.dart';

Uygulamanın geri kalanı yalnızca mqtt_factory.dart ve package:mqtt_client/mqtt_client.dart'ı import eder ve ortak MqttClient taban sınıfıyla çalışır. Connect, subscribe, publish ve updates stream'i iki tarafta da aynı davranır.

Yeniden bağlanma ve uygulama yaşam döngüsü

Mobil ağlar bağlantıyı sürekli koparır: Wi-Fi'dan hücresel ağa geçiş, tüneller, işletim sisteminin uygulamanızı askıya alması. mqtt_client yerleşik bir otomatik yeniden bağlanma özelliğine sahiptir:

Otomatik yeniden bağlanma
client
  ..autoReconnect = true
  ..resubscribeOnAutoReconnect = true
  ..onAutoReconnect = () => print('bağlantı koptu, yeniden bağlanılıyor...')
  ..onAutoReconnected = () => print('yeniden bağlandı');

resubscribeOnAutoReconnect (varsayılanı true) ile istemci yeniden bağlandıktan sonra aboneliklerinizi tekrar gönderir. Bu önemlidir, çünkü clean session broker tarafında abonelikleri unutur. Clean session'da broker, bağlantı kopukken yayınlanan mesajları saklamaz; uygulama veri kaçırmamalıysa kalıcı oturumla bağlanın (startClean() kullanmayın, sabit bir client ID tutun) ve QoS 1 ile abone olun. Bunu kendi özel broker instance'ınızda test edin: paylaşılan test endpoint'i çevrimdışı oturumları saklamaz.

iOS ve Android'in arka plandaki uygulamaları askıya aldığını ve bunun keep-alive'dan bağımsız olarak soketi kapattığını unutmayın. Yaşam döngüsünü WidgetsBindingObserver ile izleyin, paused durumunda düzgünce bağlantıyı kesin, resumed durumunda yeniden bağlanın. Arka planda teslim için doğru araç push bildirimleridir; Android'de arka plan bağlantı kopmaları ve iOS arka plan sınırları yazılarına bakın.

Platform kontrol listesi

  • Android: android/app/src/main/AndroidManifest.xml dosyasına <uses-permission android:name="android.permission.INTERNET"/> ekleyin. Debug build'ler bunu otomatik içerir; "debug'da çalışıyor, release'de çalışmıyor" durumunun bu kadar yaygın olmasının sebebi budur.
  • macOS: DebugProfile.entitlements ve Release.entitlements dosyalarının ikisine de true değeriyle com.apple.security.network.client ekleyin; aksi halde sandbox giden soketleri engeller.
  • iOS: TLS için ek bir şey gerekmez. App Transport Security URL yükleme API'leri için geçerlidir, ham TCP veya bir hostname'e ws:// için değil; yine de production'da TLS kullanın.
  • Web: MqttBrowserClient, wss://, doğru port ve path kullanın. Tarayıcı mixed content'i (bir https:// sayfasından ws:// broker'a bağlantı) engeller.

Uçtan uca test

Uygulamayı çalıştırın, tarayıcıda online MQTT istemcisini açın, güvenli WebSocket ile broker.connectmq.com'a bağlanın ve testmqtt/flutter-demo/# topic'ine abone olun. Uygulamanızın yayınladığı ölçümleri görmeniz gerekir; tarayıcıdan testmqtt/flutter-demo/sensors/test topic'ine yayınladığınız her şey de Flutter debug konsolunda görünmelidir.

Flutter uygulamanızın mesajlarını tarayıcıda izleyin

Hiçbir şey gelmiyorsa şu sırayla kontrol edin: connect() sonrasındaki bağlantı durumu, abonelik topic'inin publish topic'iyle eşleşip eşleşmediği (wildcard'lar için topic matcher yardımcı olur) ve client.logging(on: true) sonrasındaki debug çıktısı. En sık görülen exception'lar ve çözümleri Flutter MQTT hataları yazısında toplanmıştır.

Tek bir factory dosyası, tek bir mesaj stream'i ve açık otomatik yeniden bağlanma ile aynı Flutter kodu telefondan, masaüstünden ve bir tarayıcı sekmesinden broker'la konuşur. Paylaşılan endpoint ile başlayın, kimlik bilgileriyle TLS ve güvenli WebSocket'e geçin ve geliştirme sırasında tarayıcı istemcisini açık tutarak uygulamanızın gönderdiği her mesajı görün.

Sıkça sorulan sorular

Flutter MQTT uygulamam neden Android'de çalışıyor da web'de çalışmıyor?

Tarayıcılar ham TCP soketi açamaz, bu yüzden MqttServerClient Flutter web'de çalışmaz. wss:// adresiyle, broker'ın WebSocket portuyla (örneğin 8084) ve /mqtt gibi doğru path ile MqttBrowserClient kullanın.

mqtt_client MQTT 5'i destekliyor mu?

Hayır. mqtt_client MQTT 3.1 ve 3.1.1'i uygular. Reason code, user property veya session expiry gibi MQTT 5 özelliklerine ihtiyacınız varsa aynı yazarın, API'si çok benzer olan mqtt5_client paketini kullanın.

Flutter'da MQTT için özel izin gerekir mi?

Android'de release build'ler AndroidManifest.xml içinde INTERNET iznine ihtiyaç duyar (debug build'ler bunu otomatik ekler). macOS'ta giden bağlantı entitlement'ını (com.apple.security.network.client) açın. iOS ve web için ek bir şey gerekmez.