포스트

C# WPF 에서 MQTTnet 5 로 MQTT 서버와 클라이언트 구성하기

C# WPF 에서 MQTTnet 5 로 MQTT 서버와 클라이언트 구성하기

## MQTT란 무엇인가? MQTT(Message Queuing Telemetry Transport)사물인터넷(Iot) 환경에서 널리 사용되는 경량 메시지 프로토콜 입니다.

처음에는 위성 통신용으로 개발되었으며, 지금은 스마트홈, 센서 네트워크, 원격제어 등 다양한 분야에서 활용됩니다.

## 특징

특징설명
경량성매우 가볍고 적은 네트워크 트래픽 사용
Pub/Sub 모델발행(Publish) / 구독(Subscribe) 구조
지속 연결클라이언트와 브로커 간 TCP 기반 연결 유지
Qos 지원메시지 전송 품질 보장 (0, 1, 2 단계)
보안TLS, 인증 기반 통신 가능

## MQTT 통신 구조

1
2
3
4
5
6
7
8
9
10
11
 +------------------------+
|      MQTT Broker       | ◀── 중개자 역할 (서버)
+------------------------+
      ▲             ▲
      │             │
+-----------+   +-----------+
| Publisher |   |Subscriber|
+-----------+   +-----------+

- Publisher: 특정 토픽(topic)에 메시지를 발행
- Subscriber: 원하는 토픽을 구독하고 메시지를 수신

우리가 만들 예제

WPF 앱에서 MQTT 브로커를 직접 실행하고, 앱 내부에서 Publish / Subscribe도 함께 동작시켜 다음을 확입하니다:

  • 메시지를 전송(Publish)
  • 구독자가 메시지를 수신(Subscribe)
  • WPF UI와 연동하여 동작 확인

## 개발 환경

  • WPF (.Net 6 이상)
  • MQTTnet 5.0.1.1416
1
2
 NuGet 설치 명령어:
 PM> Install-Package MQTTnet -Version 5.0.1.1416

## 예제 코드 - UI (MainWindow.xaml)

1
2
3
4
5
6
7
8
 <Window x:Class="MqttTest.MainWindow"
        xmlns="http://schemas.microsoft.com/winfx/2006/xaml/presentation"
        Title="MQTT Demo" Height="200" Width="400">
    <StackPanel Margin="20">
        <TextBox x:Name="MessageTextBox" Height="30" Margin="0,0,0,10" />
        <Button Content="메시지 보내기" Click="Send_Click" Height="30"/>
    </StackPanel>
</Window>

## 예제 코드 - 로직 (MainWindow.xaml.cs)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
 using MQTTnet;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System.Text;
using System.Windows;

namespace MqttTest
{
    public partial class MainWindow : Window
    {
        private MqttServer mqttServer;
        private IMqttClient subscriberClient;
        private IMqttClient publisherClient;

        public MainWindow()
        {
            InitializeComponent();
            StartMqttServerAndClients();
        }

        private async void StartMqttServerAndClients()
        {
            await StartMqttServerAsync();
            await StartSubscriberAsync();
            await StartPublisherAsync();

            await Task.Delay(1000); // subscriber 연결 대기
            await PublishTestMessageAsync("test/topic", "Hello from Publisher");
        }

        private async Task StartMqttServerAsync()
        {
            var options = new MqttServerOptionsBuilder()
                .WithDefaultEndpoint()
                .Build();

            var serverFactory = new MqttServerFactory();
            mqttServer = serverFactory.CreateMqttServer(options);

            await mqttServer.StartAsync();
        }

        private async Task StartSubscriberAsync()
        {
            var clientFactory = new MqttClientFactory();
            subscriberClient = clientFactory.CreateMqttClient();

            subscriberClient.ApplicationMessageReceivedAsync += e =>
            {
                string topic = e.ApplicationMessage.Topic;
                string payload = Encoding.UTF8.GetString(e.ApplicationMessage.Payload);

                Dispatcher.Invoke(() =>
                {
                    MessageBox.Show($"[SUBSCRIBER] 수신된 메시지: {payload} (Topic: {topic})");
                });

                return Task.CompletedTask;
            };

            var options = new MqttClientOptionsBuilder()
                .WithTcpServer("127.0.0.1", 1883)
                .WithClientId("subscriber-client")
                .Build();

            await subscriberClient.ConnectAsync(options);
            await subscriberClient.SubscribeAsync("test/topic");
        }

        private async Task StartPublisherAsync()
        {
            var clientFactory = new MqttClientFactory();
            publisherClient = clientFactory.CreateMqttClient();

            var options = new MqttClientOptionsBuilder()
                .WithTcpServer("127.0.0.1", 1883)
                .WithClientId("publisher-client")
                .Build();

            await publisherClient.ConnectAsync(options);
        }

        private async Task PublishTestMessageAsync(string topic, string message)
        {
            var appMessage = new MqttApplicationMessageBuilder()
                .WithTopic(topic)
                .WithPayload(Encoding.UTF8.GetBytes(message))
                .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
                .Build();

            await publisherClient.PublishAsync(appMessage);
        }

        private async void Send_Click(object sender, RoutedEventArgs e)
        {
            string topic = "test/topic";
            string payload = MessageTextBox.Text;

            if (string.IsNullOrWhiteSpace(payload))
            {
                MessageBox.Show("메시지를 입력하세요.");
                return;
            }

            var appMessage = new MqttApplicationMessageBuilder()
                .WithTopic(topic)
                .WithPayload(Encoding.UTF8.GetBytes(payload))
                .WithQualityOfServiceLevel(MqttQualityOfServiceLevel.AtLeastOnce)
                .Build();

            await publisherClient.PublishAsync(appMessage);
        }
    }
}

## 실행 결과

  • 앱 실행 시 MQTT 브로커가 백그라운드에서 실행됨
  • Publish 와 Subscribe 가 자동 연결됨
  • 메시지를 보내면 MessageBox로 수신 확인 가능

## 마무리 (외부 MQTT 브로커와 연동하기) 지금까지는 MQTTnet 라이브러리를 이용해 WPF 앱 내부에서 브로커를 직접 실행했지만,
실제 현업 환경에서는 전용 MQTT 브로커를 따로 운영하는 경우가 대부분입니다.
대분적인 MQTT 브로커는 다음과 같습니다:

브로커특징
Mosquitto가볍고 빠르며 설치 쉬움 (IoT에서 가장 많이 사용됨
RabbitMQ + MQTT 플러그인고성능 메시지 브로커, MQTT뿐 아니라 AMQP 등 지원
EMQX고급 분산 MQTT 브로커, 대규모 서비스에 적합
HiveMQ상업용 중심, 웹 UI 및 클라우드 지원 강력
이 기사는 저작권자의 CC BY 4.0 라이센스를 따릅니다.