IoT Eğitimleri

React Native MQTT: WebSocket ile Gerçek Zamanlı Mobil IoT Uygulamaları

MQTT.js ve güvenli WebSocket ile bir React Native MQTT uygulaması geliştirin: yeniden kullanılabilir bir hook, canlı cihaz verisi, komutlar ve uygulama arka plan ile ön plan arasında geçerken doğru yeniden bağlanma yönetimi.

Güvenli WebSocket üzerinden bulut broker'dan canlı IoT verisi alan, Android ve iPhone üzerinde çalışan React Native MQTT uygulaması
Bunu kendi MQTT broker’ınızda çalıştırınFree planla özel, yönetilen bir MQTT broker alın ve hazır kod örnekleriyle başlayın.
Ücretsiz deneyin Kod örnekleri Kart gerekmez

React Native MQTT, mobil uygulamanıza bir API'yi sürekli sorgulamadan canlı cihaz verisi sağlar. Bu eğitimde bir React Native (veya Expo) uygulamasını MQTT.js ile güvenli WebSocket üzerinden bir bulut broker'a bağlayacak, bunu yeniden kullanılabilir bir hook içine saracak, bir cihaza komut gönderecek ve ilk denemelerin çoğunu bozan arka plan ile ön plan geçişlerini yöneteceksiniz.

React Native MQTT için Neden WebSocket?

React Native'in JavaScript çalışma ortamında ham TCP soketleri yoktur. Bunları yerel bir modülle ekleyebilirsiniz, ancak bu yerel derleme adımları, Expo dev client'ları ve platforma özgü hatalar demektir. React Native ise standartlara uygun bir WebSocket ile gelir ve MQTT.js, MQTT'yi bunun üzerinde çalıştırmayı zaten bilir. Sonuç, Expo Go'da, Android'de ve iOS'ta hiç yerel kod olmadan çalışan saf JavaScript bir client'tır.

ConnectMQ, herkesçe güvenilen bir sertifikayla 8084 portunda /mqtt yoluyla güvenli WebSocket sunar; dolayısıyla ihtiyacınız olan URL şudur:

wss://broker.connectmq.com:8084/mqtt

8084 portu HTTPS'e benzediği için çoğu kurumsal ağdan ve mobil operatör ağından da geçer. Tüm arka plan bilgisi için WebSocket üzerinden MQTT yazısına bakın.

Proje Kurulumu

Bir proje oluşturun, MQTT.js'i ve bir URL polyfill'ini kurun (React Native'in yerleşik URL uygulaması eksiktir ve MQTT.js broker URL'sini ayrıştırır):

npx create-expo-app@latest mqtt-demo
cd mqtt-demo
npx expo install react-native-url-polyfill
npm install mqtt

MQTT.js 5, paket export'larında kendi içinde bağımsız tarayıcı paketini işaret eden bir react-native girişi tanımlar; bu yüzden yeni Metro sürümleri import mqtt from "mqtt" ifadesini doğru çözümler. Paketleyiciniz bunun yerine Node derlemesini seçerse (net, tls veya fs ile ilgili hatalar görürsünüz), paketi açıkça import mqtt from "mqtt/dist/mqtt.esm" ile import edin.

Giriş dosyanızın en üstünde (Expo Router'da app/_layout.tsx, yalın bir uygulamada index.js) polyfill'i bir kez yükleyin:

import "react-native-url-polyfill/auto";

Bir React Native MQTT Hook'u Oluşturma

Özel bir hook, bağlantı mantığını bileşenlerin dışında tutar ve tek bir client'ı ekranlar arasında paylaşmayı kolaylaştırır. Bu sürüm mount anında bağlanır, subscribe olur, mesajları ve bağlantı durumunu sunar ve AppState değişikliklerine tepki verir.

hooks/useMqtt.js:

import { useEffect, useRef, useState, useCallback } from "react";
import { AppState } from "react-native";
import mqtt from "mqtt";

const BROKER_URL = "wss://broker.connectmq.com:8084/mqtt";

export function useMqtt({ topics, username, password, clientId }) {
  const clientRef = useRef(null);
  const [status, setStatus] = useState("idle");
  const [messages, setMessages] = useState({});
  const idRef = useRef(clientId || `rn-${Math.random().toString(16).slice(2, 10)}`);

  const connect = useCallback(() => {
    if (clientRef.current) return;

    const client = mqtt.connect(BROKER_URL, {
      clientId: idRef.current,
      username,
      password,
      protocolVersion: 5,
      clean: true,
      keepalive: 30,
      reconnectPeriod: 2000,
      connectTimeout: 10000,
    });
    clientRef.current = client;
    setStatus("connecting");

    client.on("connect", () => {
      setStatus("connected");
      client.subscribe(topics, { qos: 1 }, (err) => {
        if (err) console.warn("subscribe failed", err.message);
      });
    });
    client.on("reconnect", () => setStatus("reconnecting"));
    client.on("offline", () => setStatus("offline"));
    client.on("error", (err) => console.warn("mqtt error", err.message));
    client.on("message", (topic, payload) => {
      setMessages((prev) => ({ ...prev, [topic]: payload.toString() }));
    });
  }, [topics, username, password]);

  const disconnect = useCallback(() => {
    const client = clientRef.current;
    clientRef.current = null;
    if (client) client.end(true);
    setStatus("idle");
  }, []);

  const publish = useCallback((topic, message) => {
    clientRef.current?.publish(topic, message, { qos: 1 });
  }, []);

  useEffect(() => {
    connect();
    const sub = AppState.addEventListener("change", (state) => {
      if (state === "active") connect();
      else if (state === "background") disconnect();
    });
    return () => {
      sub.remove();
      disconnect();
    };
  }, [connect, disconnect]);

  return { status, messages, publish, clientId: idRef.current };
}

client.end(true) ifadesine dikkat edin: true, yoldaki mesajları beklemek yerine soketi hemen kapanmaya zorlar; bu önemlidir, çünkü işletim sistemi uygulamayı askıya almadan önce size yalnızca birkaç saniye tanır.

Arka Plan ve Ön Plan Yeniden Bağlanmalarını Yönetme

React Native MQTT uygulamalarının çoğu tam da burada hata yapar; bu yüzden bu konu kendi bölümünü hak ediyor. Bir telefonda gerçekte olan şudur:

  1. Kullanıcı uygulama değiştirir. iOS saniyeler içinde JavaScript thread'inizi askıya alır; Android onu kısa bir süre canlı tutabilir, ardından ağ erişimini kısıtlar.
  2. WebSocket uygulamanın bakış açısından "açık" kalır, ancak keep-alive ping'leri gönderilmez. Broker, oturumu yaklaşık 1,5 keep-alive aralığından sonra kapatır.
  3. Kullanıcı geri döner. MQTT.js ölü soketi sonunda fark eder, ama yalnızca kendi zaman aşımından sonra; bu yüzden arayüz birkaç saniye boyunca eski değerleri ve "connected" durumunu gösterir.

Hook bunu background durumunda bilinçli olarak bağlantıyı kapatıp active durumunda yeni bir bağlantı açarak önler. Birkaç kural daha yardımcı olur:

  • connect işleyicisinin içinde subscribe olun, client'ı oluşturduktan sonra bir kez değil. MQTT.js kendi yeniden bağlanmalarında varsayılan olarak yeniden subscribe olur, ancak ön plana dönüldükten sonra oluşturulan yepyeni bir client yeni bir subscribe gerektirir; bunu işleyicide yapmak iki durumu da kapsar.
  • Durum için retained mesajlar kullanın. Cihazlar en son durumlarını retain bayrağıyla publish ederse, yeni bağlanan bir uygulama bir sonraki güncellemeyi beklemeden güncel değeri hemen alır. Bkz. MQTT retained mesajları.
  • Uyarılar için arka planda bağlantı tutmayın. Kapalı bir uygulamaya ulaşması gereken her şey için platform push bildirimlerini kullanın; MQTT, uygulama açıkken canlı veri içindir.
  • iOS'ta inactive durumunu yok sayın. Bildirim merkezi veya gelen bir arama için tetiklenir; orada bağlantıyı kapatmak gereksiz yeniden bağlanma karmaşasına yol açar.
AppState Hook ne yapar Neden
active client yoksa connect() Taze soket ve subscription'lar
inactive (yalnızca iOS) hiçbir şey Geçici, ör. kontrol merkezi aşağı çekildi
background disconnect() İşletim sistemi soketi zaten askıya alacak

Hook'u Bir Ekranda Kullanma

import { View, Text, Switch, StyleSheet } from "react-native";
import { useMemo, useState } from "react";
import { useMqtt } from "../hooks/useMqtt";

const PREFIX = "connectmq/demo/rn-7f3a91";   // kendi rastgele önekinizi seçin

export default function DeviceScreen() {
  const topics = useMemo(() => [`${PREFIX}/pump/status`, `${PREFIX}/tank/level`], []);
  const { status, messages, publish } = useMqtt({ topics });
  const [pumpOn, setPumpOn] = useState(false);

  const level = messages[`${PREFIX}/tank/level`] ?? "--";
  const pumpStatus = messages[`${PREFIX}/pump/status`] ?? "unknown";

  return (
    <View style={styles.container}>
      <Text style={styles.status}>Broker: {status}</Text>
      <Text style={styles.value}>{level} %</Text>
      <Text>Tank level</Text>
      <Text style={styles.row}>Pump reports: {pumpStatus}</Text>
      <View style={styles.row}>
        <Text>Pump command</Text>
        <Switch
          value={pumpOn}
          disabled={status !== "connected"}
          onValueChange={(v) => {
            setPumpOn(v);
            publish(`${PREFIX}/pump/set`, v ? "ON" : "OFF");
          }}
        />
      </View>
    </View>
  );
}

const styles = StyleSheet.create({
  container: { flex: 1, padding: 24, justifyContent: "center", gap: 8 },
  status: { color: "#666" },
  value: { fontSize: 56, fontWeight: "600" },
  row: { marginTop: 16, flexDirection: "row", justifyContent: "space-between", alignItems: "center" },
});

Topic dizisindeki useMemo önemlidir: o olmadan her render'da oluşan yeni dizi connect callback'ini değiştirir ve yeniden bağlanma döngülerine neden olur.

Tek Bir Bağlantıyı Ekranlar Arasında Paylaşma

Hook, yazıldığı haliyle onu kullanan her bileşen için bir bağlantı açar. Birkaç ekranlı bir uygulamada client'ı kök seviyedeki bir React context provider'a taşıyın: provider, topic'lerin birleşimiyle useMqtt'yi bir kez çağırır ve messages ile publish'i context üzerinden sunar; her ekran yalnızca ihtiyaç duyduğu topic'leri okur. Uygulama başına tek bağlantı sizi bağlantı limitlerinin rahatça içinde tutar (ücretsiz Serverless planı toplamda 25 eşzamanlı bağlantıya izin verir) ve broker'ın tek bir telefondan birbiriyle yarışan birkaç oturum görmesini önler. Ekranların zamanla farklı subscription'lara ihtiyacı olursa, yeniden bağlanmak yerine context'e subscribe ve unsubscribe fonksiyonları ekleyin.

Public Broker'a Karşı Test

broker.connectmq.com adresindeki ücretsiz public MQTT broker anonim client'ları kabul eder; bu yüzden hesap olmadan test edebilirsiniz. Public alan herkes tarafından okunabildiği için rastgele öneki koruyun. Tankı ve pompayı bir terminalden simüle edin:

PREFIX="connectmq/demo/rn-7f3a91"

# Retained, böylece uygulama bağlandıktan hemen sonra görür
mosquitto_pub -h broker.connectmq.com -p 1883 -i rn-sim-tank -r \
  -t "$PREFIX/tank/level" -m "64"

mosquitto_pub -h broker.connectmq.com -p 1883 -i rn-sim-pump -r \
  -t "$PREFIX/pump/status" -m "OFF"

mosquitto_sub -h broker.connectmq.com -p 1883 -i rn-sim-cmd -t "$PREFIX/pump/set" -v

Uygulamayı bir dakikalığına arka plana alın, geri getirin ve durumun connecting'den connected'a geçtiğini ve retained değerlerin anında yeniden göründüğünü doğrulayın. Public broker client başına saniyede en fazla 10 mesaja izin verir; bu bir arayüz için fazlasıyla yeterlidir.

Üretim: Özel Broker ve Kimlik Bilgileri

Yayınlanan bir uygulama için yönetilen bir MQTT broker üzerinde özel bir broker'a geçin:

  1. ConnectMQ portalında bir broker oluşturun ve üretilen kullanıcı adını, parolayı ve client ID'yi kopyalayın.
  2. Bunları hook'a iletin: useMqtt({ topics, username: "YOUR_USERNAME", password: "YOUR_PASSWORD", clientId: "YOUR_CLIENT_ID" }).
  3. Gizli bilgileri expo-secure-store veya platform keychain'i ile saklayın; herkesin açabileceği JavaScript paketinde asla saklamayın.

ConnectMQ client ID'leri verdiği kimlik bilgilerine bağlar; bu yüzden iki telefon aynı anda tek bir kimliği paylaşamaz. Çok kullanıcılı bir uygulamada backend'inizin, oturum açtıktan sonra her kullanıcıya kendi kimlik bilgilerini vermesini sağlayın. Ücretsiz Serverless planı en fazla 25 eşzamanlı bağlantıya izin verir; bu bir ekip pilotu için yeterlidir; ücretli Serverless planları 1.000'e kadar çıkar.

Sorun Giderme

Belirti Neden Çözüm
Cannot read property 'protocol' of undefined veya URL hataları URL polyfill'i eksik Önce react-native-url-polyfill/auto import edin
net veya tls içeren hatalar Metro Node derlemesini çözümledi mqtt/dist/mqtt.esm import edin
Simülatörde bağlanıyor, telefonda zaman aşımı Yanlış port veya wss:// yerine ws:// wss://...:8084/mqtt kullanın
Her render'da yeniden bağlanma döngüsü Kararsız topics dizisi useMemo içine sarın
Devam ettikten sonra eski veri Soket arka planda öldü background durumunda bağlantıyı kesin, active durumunda yeniden bağlanın

ConnectMQ ile Başlayın

React Native uygulamanızı dakikalar içinde özel bir broker'a yönlendirin. app.connectmq.com adresinde kaydolun, kredi kartı gerekmeden ücretsiz bir Serverless broker oluşturun ve uygulamanızın komutlarının geldiğini tarayıcı içi client ve canlı topic explorer ile izleyin. ConnectMQ TLS'i, ölçeklendirmeyi ve broker operasyonlarını üstlenir; böylece sürdürdüğünüz tek MQTT kodu hook'unuz olur. Diğer yığınlar için MQTT client kütüphaneleri rehberi ve Flutter MQTT eğitimi aynı deseni gösterir.

Sık Sorulan Sorular

React Native, WebSocket yerine TCP üzerinden MQTT kullanabilir mi?

Yalnızca yerel bir TCP soket modülüyle; bu da yerel derleme karmaşıklığı ekler. 8084 portundaki güvenli WebSocket, MQTT.js ile kutudan çıktığı gibi çalışır ve daha basit, daha taşınabilir bir seçimdir.

Bu Expo Go ile çalışır mı?

Evet. WebSocket üzerinden MQTT.js saf JavaScript'tir; bu yüzden özel bir dev client olmadan Expo Go'da çalışır.

React Native MQTT bağlantım neden arka planda kopuyor?

Mobil işletim sistemleri arka plandaki uygulamaları ve ağ soketlerini askıya alır. background durumunda bağlantıyı kesin, active durumunda yeniden bağlanın ve kapalı bir uygulamaya ulaşması gereken uyarılar için push bildirimleri kullanın.

React Native'den MQTT 5 mi yoksa 3.1.1 mi kullanmalıyım?

Her ikisi de ConnectMQ ile çalışır. MQTT 5, bir şey başarısız olduğunda daha net reason code'lar verir; bu da mobilde bağlantı sorunlarını ayıklamayı kolaylaştırır.

İlgili yazılar

Test, Betik ve Hata Ayıklama için En İyi MQTT CLI Araçları

mosquitto_pub/sub, MQTTX CLI, HiveMQ MQTT CLI, MQTT.js CLI, curl ve mqttui'nin uygulamalı karşılaştırması; betik yazma ve hata ayıklama için doğru aracı seçebilmeniz için aynı test broker.connectmq.com üzerinde çalıştırıldı.

7 dk okuma

Tüm yazılar →

Özel MQTT broker’ınız birkaç tık uzağınızda

Hesabınızı oluşturun, broker kimlik bilgilerinizi alın ve ilk cihazınızı bağlayın — kurulacak, yamalanacak ya da ölçeklendirilecek sunucu yok.