IoT Eğitimleri

MQTTnet C# Eğitimi: .NET'te TLS ile MQTT 5 İstemcileri Geliştirin

MQTTnet, MQTT 5 özellikleri ve 8883 portunda TLS ile bir .NET konsol publisher ve subscriber geliştirin; ardından yeniden bağlanma mantığı ekleyip aynı kodu özel bir ConnectMQ broker'a taşıyın.

Bir bulut MQTT broker'a sensör verisi yayınlayan mqttnet istemcisini gösteren C# kod düzenleyicisi
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

MQTTnet, .NET için en yaygın kullanılan MQTT kütüphanesidir ve hızlı bir konsol testinden üretim ortamındaki bir servise kadar her şeyi kapsar. Bu eğitimde MQTTnet ile bir C# publisher ve subscriber geliştirecek, MQTT 5 özelliklerini kullanacak, bağlantıyı TLS ile şifreleyecek ve güvenebileceğiniz bir yeniden bağlanma mantığı ekleyeceksiniz. Her örnek ücretsiz herkese açık broker broker.connectmq.com üzerinde çalışır; böylece .NET SDK dışında hiçbir şey kurmadan takip edebilirsiniz.

Neye İhtiyacınız Var?

  • .NET 8 SDK veya daha yenisi (kontrol etmek için dotnet --version)
  • Herhangi bir düzenleyici: Visual Studio, Rider veya C# eklentili VS Code
  • 8883 (TLS) veya 1883 (düz TCP) portuna giden erişimi olan bir terminal

İlk bölüm için bir hesaba ihtiyacınız yok. broker.connectmq.com adresindeki herkese açık MQTT test broker, boş olmayan ve benzersiz herhangi bir client ID ile anonim istemcileri kabul eder. Herkese açık alanın herkes tarafından okunabildiğini unutmayın; bu yüzden rastgele bir topic öneki kullanın ve oraya asla gerçek veri göndermeyin.

MQTTnet Sürümleri: Önce Bunu Okuyun

MQTTnet, 5. sürümde büyük bir API temizliğinden geçti. Eski blog yazılarından kod parçaları kopyalarsanız derleme hatalarıyla karşılaşırsınız. En önemli farklar:

Konu MQTTnet 4.x MQTTnet 5.x
Factory sınıfı MqttFactory MqttClientFactory
Namespace'ler MQTTnet.Client, MQTTnet Büyük ölçüde MQTTnet altında düzleştirildi
Managed client (otomatik yeniden bağlanma) MQTTnet.Extensions.ManagedClient Kaldırıldı, kendi döngünüzü yazın
Payload tipi byte[] / ArraySegment<byte> ReadOnlySequence<byte>
TLS kurulumu WithTls() (eskimiş) veya WithTlsOptions WithTlsOptions(o => o.UseTls())

Bu rehber MQTTnet 5'i hedefler. Kavramlar 4.x'e de aktarılabilir, ancak sınıf adları yukarıda gösterildiği gibi farklıdır. Daha derine inmeniz gerekirse resmi MQTTnet deposu her özellik için örnekler içerir.

Projeleri Oluşturun

İki konsol uygulamasıyla tek bir solution oluşturun:

mkdir MqttNetDemo && cd MqttNetDemo
dotnet new console -n Publisher
dotnet new console -n Subscriber
dotnet add Publisher package MQTTnet
dotnet add Subscriber package MQTTnet

Şimdi benzersiz bir topic öneki seçin ve her iki uygulamada da kullanın. connectmq/demo/k7f3q9/ gibi bir şey yeterlidir. Paylaşılan bir broker'daki çakışmalar, "hiç göndermediğim mesajları neden alıyorum?" kafa karışıklığının klasik bir kaynağıdır.

İlk MQTTnet Subscriber'ınız

Subscriber/Program.cs dosyasını aşağıdaki kodla değiştirin. Kod, 8883 portunda TLS üzerinden bağlanır, MQTT 5 kullanır, QoS 1 ile abone olur ve her mesajı MQTT 5 özellikleriyle birlikte yazdırır.

using System.Text;
using MQTTnet;
using MQTTnet.Formatter;
using MQTTnet.Protocol;

const string Prefix = "connectmq/demo/k7f3q9";

var factory = new MqttClientFactory();
using var client = factory.CreateMqttClient();

var options = new MqttClientOptionsBuilder()
    .WithTcpServer("broker.connectmq.com", 8883)
    .WithClientId($"sub-{Guid.NewGuid():N}")
    .WithProtocolVersion(MqttProtocolVersion.V500)
    .WithTlsOptions(o => o.UseTls())
    .WithCleanStart(true)
    .WithKeepAlivePeriod(TimeSpan.FromSeconds(30))
    .Build();

client.ApplicationMessageReceivedAsync += e =>
{
    var msg = e.ApplicationMessage;
    var payload = msg.ConvertPayloadToString();
    Console.WriteLine($"[{DateTime.Now:HH:mm:ss}] {msg.Topic} (QoS {(int)msg.QualityOfServiceLevel}, retain={msg.Retain})");
    Console.WriteLine($"  payload: {payload}");
    if (!string.IsNullOrEmpty(msg.ContentType))
        Console.WriteLine($"  content-type: {msg.ContentType}");
    foreach (var p in msg.UserProperties ?? [])
        Console.WriteLine($"  user-property: {p.Name}={p.Value}");
    return Task.CompletedTask;
};

var result = await client.ConnectAsync(options, CancellationToken.None);
Console.WriteLine($"Connected: {result.ResultCode}");

var subscribe = new MqttClientSubscribeOptionsBuilder()
    .WithTopicFilter(f => f
        .WithTopic($"{Prefix}/#")
        .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce))
    .Build();

var subResult = await client.SubscribeAsync(subscribe, CancellationToken.None);
foreach (var item in subResult.Items)
    Console.WriteLine($"Subscribed {item.TopicFilter.Topic}: {item.ResultCode}");

Console.WriteLine("Waiting for messages. Press Enter to exit.");
Console.ReadLine();

await client.DisconnectAsync(new MqttClientDisconnectOptionsBuilder()
    .WithReason(MqttClientDisconnectOptionsReason.NormalDisconnection)
    .Build());

dotnet run --project Subscriber ile çalıştırın. Connected: Success çıktısını ve abonelik için verilen QoS değerini görmelisiniz. Handler'ın ConnectAsync öncesinde kaydedildiğine dikkat edin; kalıcı bir oturumla abone olursanız mesajlar bağlantı açıldığı anda gelebilir ve geç kaydedilen bir handler bunları kaçırır.

MQTTnet ve MQTT 5 Özellikleriyle Yayın Yapmak

Şimdi sıra Publisher/Program.cs dosyasında. Bu kod, her iki saniyede bir content type, bir user property ve mesaj sona erme aralığı (message expiry interval) içeren bir JSON okuması ve buna ek olarak retained bir durum mesajı gönderir.

using System.Text.Json;
using MQTTnet;
using MQTTnet.Formatter;
using MQTTnet.Protocol;

const string Prefix = "connectmq/demo/k7f3q9";

var factory = new MqttClientFactory();
using var client = factory.CreateMqttClient();

var options = new MqttClientOptionsBuilder()
    .WithTcpServer("broker.connectmq.com", 8883)
    .WithClientId($"pub-{Guid.NewGuid():N}")
    .WithProtocolVersion(MqttProtocolVersion.V500)
    .WithTlsOptions(o => o.UseTls())
    .WithWillTopic($"{Prefix}/sensor-1/status")
    .WithWillPayload("offline")
    .WithWillRetain(true)
    .WithWillQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
    .Build();

await client.ConnectAsync(options, CancellationToken.None);

// Retained "online" durumu: yeni aboneler bunu hemen görür.
await client.PublishAsync(new MqttApplicationMessageBuilder()
    .WithTopic($"{Prefix}/sensor-1/status")
    .WithPayload("online")
    .WithRetainFlag(true)
    .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
    .Build());

var rnd = new Random();
using var cts = new CancellationTokenSource();
Console.CancelKeyPress += (_, e) => { e.Cancel = true; cts.Cancel(); };

while (!cts.IsCancellationRequested)
{
    var reading = new
    {
        deviceId = "sensor-1",
        temperature = Math.Round(20 + rnd.NextDouble() * 5, 2),
        ts = DateTimeOffset.UtcNow.ToUnixTimeMilliseconds()
    };

    var message = new MqttApplicationMessageBuilder()
        .WithTopic($"{Prefix}/sensor-1/telemetry")
        .WithPayload(JsonSerializer.Serialize(reading))
        .WithContentType("application/json")
        .WithUserProperty("firmware", "1.4.2")
        .WithMessageExpiryInterval(60)
        .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
        .Build();

    var pub = await client.PublishAsync(message, cts.Token);
    Console.WriteLine($"Published {reading.temperature} -> {pub.ReasonCode}");

    try { await Task.Delay(2000, cts.Token); } catch (TaskCanceledException) { }
}

await client.DisconnectAsync();

Önce subscriber'ı, ardından dotnet run --project Publisher komutunu çalıştırın. Subscriber JSON'u, application/json content type'ını ve firmware user property'sini yazdırır. Publisher'ı Ctrl+C ile durdurduğunuzda bağlantı düzgün kapatıldığı için retained durum online olarak kalır. Bunun yerine süreci sonlandırırsanız (terminal penceresini kapatarak), broker Last Will mesajını yayınlar ve retained durumu offline olarak değiştirir. Bu desen, MQTT'deki en basit güvenilir varlık (presence) göstergesidir.

PublishAsync sonucu, QoS 1 ve 2 için bir reason code içerir. MQTT 5 ile sessiz bir hata yerine QuotaExceeded (0x97) gibi anlamlı kodları okuyabilirsiniz; bunları her gerçek uygulamada loglamaya değer. Her seviyenin neyi garanti ettiğini hatırlamak isterseniz MQTT QoS seviyeleri açıklaması yazısına bakın.

Managed Client Olmadan Yeniden Bağlanma

MQTTnet 5 managed client'ı kaldırdığı için yeniden bağlanma sizin sorumluluğunuzdadır. Küçük ve sağlam bir desen, IsConnected durumunu kontrol eden ve yarı açık bağlantıları tespit etmek için TryPingAsync kullanan bir arka plan döngüsüdür:

async Task KeepConnectedAsync(IMqttClient client, MqttClientOptions options,
    Func<Task> onConnected, CancellationToken ct)
{
    var delay = TimeSpan.FromSeconds(1);
    while (!ct.IsCancellationRequested)
    {
        try
        {
            if (!await client.TryPingAsync(ct))
            {
                await client.ConnectAsync(options, ct);
                await onConnected();          // burada yeniden abone olun
                Console.WriteLine("Reconnected");
                delay = TimeSpan.FromSeconds(1);
            }
        }
        catch (Exception ex)
        {
            Console.WriteLine($"Connect failed: {ex.Message}");
            delay = TimeSpan.FromSeconds(Math.Min(delay.TotalSeconds * 2, 60));
        }
        await Task.Delay(delay + TimeSpan.FromMilliseconds(Random.Shared.Next(0, 500)), ct);
    }
}

Burada üç ayrıntı önemlidir. Birincisi, oturum sürdürülmediğinde onConnected içinde yeniden abone olun; kalıcı oturumlar kullanıyorsanız ConnectAsync sonucundaki result.IsSessionPresent değerini kontrol edin. İkincisi, jitter'lı üstel geri çekilme, bir ağ aksaklığından sonra istemci filosunun aynı anda yeniden bağlanmasını önler. Üçüncüsü, kalıcı bir oturuma güveniyorsanız client ID'yi yeniden bağlanmalar arasında sabit tutun; onu döngü içinde değil, başlangıçta bir kez oluşturun.

Yaygın MQTTnet Hataları ve Çözümleri

Belirti Olası neden Çözüm
Bağlanırken MqttCommunicationTimedOutException 8883 veya 1883 portu güvenlik duvarı ya da kurumsal proxy tarafından engelleniyor Test-NetConnection broker.connectmq.com -Port 8883 ile test edin; diğer portu deneyin
TLS el sıkışması sırasında AuthenticationException Eski işletim sistemi kök deposu veya yanlış sistem saati İşletim sistemi trust store'unu güncelleyin ve saati senkronize edin; broker bir Let's Encrypt sertifikası kullanır
Connect, NotAuthorized / BadUserNameOrPassword döndürüyor Yanlış kimlik bilgileri veya kimlik bilgilerine bağlı olmayan client ID Kullanıcı adını, parolayı ve client ID'yi portaldan birebir kopyalayın
Handler hiç tetiklenmiyor Handler subscribe sonrasında kaydedilmiş veya topic filtresinde yazım hatası Connect öncesinde kaydedin; subResult.Items değerini yazdırın
İstemci birkaç saniyede bir bağlantıyı kesiyor Aynı client ID'yi kullanan iki süreç Broker eski oturumu düşürür; ID'leri benzersiz yapın
QuotaExceeded (0x97) Plan veya herkese açık broker sınırına ulaşıldı Hızı düşürün veya daha yüksek sınırlara sahip bir plana geçin

Test sırasında herkese açık broker sınırlarına dikkat edin: mesaj başına 64 KB, istemci başına saniyede 10 mesaj, istemci başına 10 abonelik ve en fazla 8 topic seviyesi. Yukarıdaki publisher bu sınırların çok altında kalır.

Özel Bir Broker'a Geçiş

Herkese açık broker denemeler için idealdir, ancak üretim trafiği kimlik bilgileri olan özel bir örneğe aittir. ConnectMQ'nun yönetilen MQTT broker hizmetinde kaydolur, bir plan seçer, bir broker oluşturur ve üretilmiş bir kullanıcı adı, parola ve client ID alırsınız. Kod değişikliği üç satırdır:

var options = new MqttClientOptionsBuilder()
    .WithTcpServer("YOUR_BROKER_HOST", 8883)
    .WithClientId("YOUR_CLIENT_ID")
    .WithCredentials("YOUR_USERNAME", "YOUR_PASSWORD")
    .WithProtocolVersion(MqttProtocolVersion.V500)
    .WithTlsOptions(o => o.UseTls())
    .Build();

Kimlik bilgilerini kaynak kodun dışında tutun: geliştirme sırasında ortam değişkenlerinden veya dotnet user-secrets ile, üretimde ise platformunuzun gizli bilgi deposundan okuyun. ConnectMQ'da client ID'ler verilen kimlik bilgilerine bağlıdır; bu yüzden sızdırılmış bir parola tek başına farklı bir istemcinin kimliğine bürünmek için yeterli değildir, ancak bir depoya düşen her şeyi yine de yenilemelisiniz.

Ücretsiz Serverless plan en fazla 25 bağlantıya, QoS 1'e kadar ve 256 KB mesajlara izin verir; bu da geliştirme için fazlasıyla yeterlidir. Ücretli Serverless bunu QoS 2, 1 MB mesajlar ve shared subscription'larla 1.000 bağlantıya çıkarır; Dedicated ise size izole bir küme sunar.

MQTTnet ile Sonraki Adımlar

Konsol uygulamaları çalıştıktan sonra aynı istemci daha büyük .NET uygulamalarına da uyar:

  1. Subscriber'ı dependency injection ile uzun süre çalışan bir servis olarak barındırın: ASP.NET Core MQTT arka plan servisi rehberine bakın.
  2. .NET MAUI MQTT uygulaması eğitimiyle platformlar arası bir mobil ve masaüstü arayüzü geliştirin.
  3. Bir Unity MQTT client ile 3D sahneleri canlı veriyle yönetin.
  4. MQTT istemci kütüphanelerine genel bakış yazısında MQTTnet'i diğer dillerdeki kütüphanelerle karşılaştırın.

Bu istemcilerin nereye bağlanacağına hâlâ karar veriyorsanız bulut MQTT broker seçme rehberi, kendi sunucunuzda barındırma ile barındırılan bir hizmet arasındaki artıları ve eksileri açıklar.

ConnectMQ ile Başlayın

MQTTnet kodunuz, özel bir broker'a yöneldiği anda üretime hazırdır. app.connectmq.com adresinde kredi kartı gerekmeden ücretsiz Serverless planda bir broker oluşturun, üretilen kimlik bilgilerini options builder'a kopyalayın ve mesajlarınızın portalın canlı topic gezgininde ve tarayıcı içi MQTT istemcisinde görünmesini izleyin.

Sık Sorulan Sorular

MQTTnet ticari kullanım için ücretsiz mi?

Evet. MQTTnet MIT lisansı altında açık kaynaklıdır ve .NET Foundation bünyesinde sürdürülür; bu nedenle ticari ürünlerde kullanabilirsiniz.

MQTTnet MQTT 5'i destekliyor mu?

Evet. User property, content type, mesaj sona erme süresi, reason code ve oturum sona erme süresi gibi MQTT 5 özelliklerini kullanmak için WithProtocolVersion(MqttProtocolVersion.V500) ayarlayın. ConnectMQ hem MQTT 3.1.1'i hem de 5.0'ı destekler.

MQTTnet managed client'a ne oldu?

MQTTnet 5'te kaldırıldı. Bu eğitimdeki gibi bir yeniden bağlanma döngüsü kullanın veya managed client'a yoğun şekilde bağımlıysanız ve henüz geçiş yapamıyorsanız MQTTnet 4.x'te kalın.

MQTTnet WebSocket üzerinden bağlanabilir mi?

Evet. WithTcpServer yerine WithWebSocketServer(o => o.WithUri("wss://broker.connectmq.com:8084/mqtt")) kullanın. Bu, bir ağdan dışarıya yalnızca HTTPS trafiğine izin verildiğinde işe yarar.

İ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.