C#应用 - 实现MQTT协议
前言
本文部分实现了MQTT协议V3.1.1版本。只关注协议核心部分,用尽量少的代码写一个简化版的MqttBroker,能够与第三方MQTT工具进行通信。
1,简介
简化版实现了Broker的核心功能:
- CONNECT/CONNACK:客户端连接管理
- PUBLISH:消息发布
- SUBSCRIBE/SUBACK:主题订阅
- PINGREQ/PINGRESP:心跳检测
- DISCONNECT:客户端断开
2,设计
2.1 基础类型
namespace 简单MqttBroker
{
/// <summary>
/// MQTT控制报文类型
/// </summary>
public enum ControlPacketType : byte
{
CONNECT = 1, // 客户端连接请求
CONNACK = 2, // 连接确认
PUBLISH = 3, // 发布消息
PUBACK = 4, // 发布确认(QoS 1)
PUBREC = 5, // 发布收到(QoS 2)
PUBREL = 6, // 发布释放(QoS 2)
PUBCOMP = 7, // 发布完成(QoS 2)
SUBSCRIBE = 8, // 订阅请求
SUBACK = 9, // 订阅确认
UNSUBSCRIBE = 10, // 取消订阅
UNSUBACK = 11, // 取消订阅确认
PINGREQ = 12, // 心跳请求
PINGRESP = 13, // 心跳响应
DISCONNECT = 14 // 断开连接
}
/// <summary>
/// MQTT客户端会话状态
/// </summary>
public enum MqttConnectionStatus
{
Disconnected, // 未连接
Connected // 已连接
}
/// <summary>
/// MQTT消息
/// </summary>
public class MqttMessage
{
public string Topic { get; set; } // 主题
public byte[] Payload { get; set; } // 消息内容
public bool Retain { get; set; } // 是否保留消息
public byte QosLevel { get; set; } // QoS级别(0,1,2)
}
/// <summary>
/// MQTT客户端会话信息
/// </summary>
public class ClientSession
{
public string ClientId { get; set; } // 客户端ID
public TcpClient TcpClient { get; set; } // TCP客户端连接
public NetworkStream NetworkStream { get; set; } // 网络流
public MqttConnectionStatus Status { get; set; } = MqttConnectionStatus.Disconnected; // 连接状态
public ConcurrentBag<string> SubscribedTopics { get; } = new ConcurrentBag<string>(); // 已订阅主题集合
public DateTime LastActivityTime { get; set; } = DateTime.UtcNow; // 最后活动时间
public SemaphoreSlim SendLock { get; } = new(1, 1); // 发送锁(防止多线程发送冲突)
public ClientSession(TcpClient tcpClient)
{
TcpClient = tcpClient ?? throw new ArgumentNullException(nameof(tcpClient));
NetworkStream = tcpClient.GetStream();
}
public void Dispose()
{
SendLock.Dispose();
NetworkStream.Dispose();
TcpClient.Dispose();
}
}
}
2.2 核心类型
namespace 简单MqttBroker
{
/// <summary>
/// MQTT Broker实现
/// </summary>
public class MqttBroker : IDisposable
{
private const byte PROTOCOL_LEVEL = 0x04; // MQTT 3.1.1
private static readonly byte[] PROTOCOL_NAME = Encoding.ASCII.GetBytes("MQTT");
private readonly TcpListener _listener; // TCP监听器
private readonly ConcurrentDictionary<string, ClientSession> _clientSessions = new(); // 客户端会话字典
private readonly ConcurrentDictionary<string, List<ClientSession>> _topicSubscriptions = new(); // 主题订阅字典
private readonly ConcurrentBag<MqttMessage> _retainedMessages = new(); // 保留消息集合
private readonly CancellationTokenSource _cts = new(); // 取消令牌源
private readonly ThreadPool _workerThreadPool; // 工作线程池
private bool _disposed; // 是否已释放资源
private const int MaxWorkerThreads = 100; // 最大工作线程数
private const int KeepAliveCheckInterval = 60000; // 保持连接检查间隔(ms)
/// <summary>
/// 构造函数
/// </summary>
/// <param name="port">监听端口</param>
public MqttBroker(int port = 1883)
{
// 配置线程池
_workerThreadPool = new ThreadPool(MaxWorkerThreads);
// 初始化TCP监听器
_listener = new TcpListener(IPAddress.Any, port);
}
/// <summary>
/// 启动Broker
/// </summary>
public void Start()
{
_listener.Start();
Debug.WriteLine($"MQTT Broker started on port {((IPEndPoint)_listener.LocalEndpoint).Port}");
// 启动客户端接收任务
_workerThreadPool.QueueUserWorkItem(_ => AcceptClientsAsync().ConfigureAwait(false));
// 启动不活跃连接清理任务
_workerThreadPool.QueueUserWorkItem(_ => CleanInactiveSessionsAsync().ConfigureAwait(false));
}
/// <summary>
/// 异步接受客户端连接
/// </summary>
private async Task AcceptClientsAsync()
{
try
{
while (!_cts.IsCancellationRequested)
{
TcpClient tcpClient = await _listener.AcceptTcpClientAsync().ConfigureAwait(false);
// 为每个客户端连接创建独立处理任务
_workerThreadPool.QueueUserWorkItem(async () =>
{
await HandleClientAsync(tcpClient).ConfigureAwait(false);
});
}
}
catch (Exception ex) when (!_cts.IsCancellationRequested)
{
Debug.WriteLine($"Accept clients error: {ex.Message}");
}
}
/// <summary>
/// 处理客户端连接
/// </summary>
private async Task HandleClientAsync(TcpClient tcpClient)
{
ClientSession session = new ClientSession(tcpClient);
try
{
while (!_cts.IsCancellationRequested && tcpClient.Connected)
{
// 接收报文
var packet = await ReadPacketAsync(session.NetworkStream);
if (packet == null)
{
break;
}
// 更新最后活动时间
session.LastActivityTime = DateTime.UtcNow;
// 处理数据包
await ProcessPacketAsync(session, packet).ConfigureAwait(false);
}
}
catch (Exception ex)
{
Debug.WriteLine($"Client {session.ClientId} error: {ex.Message}");
}
finally
{
DisconnectClient(session);
}
}
/// <summary>
/// 从网络流中读取MQTT数据包
/// </summary>
private async Task<byte[]> ReadPacketAsync(NetworkStream stream)
{
// 读取固定头(至少2字节)
var fixedHeader = new byte[2];
var bytesRead = await stream.ReadAsync(fixedHeader, 0, 2, _cts.Token).ConfigureAwait(false);
if (bytesRead < 2)
{
return null;
}
// 解析剩余长度
int remainingLength = 0;
int multiplier = 1;
byte digit;
int offset = 1;
do
{
digit = fixedHeader[offset++];
remainingLength += (digit & 127) * multiplier;
multiplier *= 128;
} while ((digit & 128) != 0 && offset < 5);
// 读取可变头和有效载荷
var packet = new byte[offset + remainingLength];
Array.Copy(fixedHeader, 0, packet, 0, offset);
if (remainingLength > 0)
{
bytesRead = await stream.ReadAsync(packet, offset, remainingLength, _cts.Token).ConfigureAwait(false);
if (bytesRead < remainingLength) return null;
}
return packet;
}
/// <summary>
/// 处理MQTT数据包
/// </summary>
private async Task ProcessPacketAsync(ClientSession session, byte[] packet)
{
var packetType = (ControlPacketType)(packet[0] >> 4);
switch (packetType)
{
case ControlPacketType.CONNECT:
await HandleConnectAsync(session, packet).ConfigureAwait(false);
break;
case ControlPacketType.PUBLISH:
await HandlePublishAsync(session, packet).ConfigureAwait(false);
break;
case ControlPacketType.SUBSCRIBE:
await HandleSubscribeAsync(session, packet).ConfigureAwait(false);
break;
case ControlPacketType.PINGREQ:
await SendPingRespAsync(session).ConfigureAwait(false);
break;
case ControlPacketType.DISCONNECT:
DisconnectClient(session);
break;
}
}
/// <summary>
/// 处理CONNECT报文
/// </summary>
private async Task HandleConnectAsync(ClientSession session, byte[] packet)
{
// 检查协议名 (偏移量4-7应为"MQTT")
var protocolName = Encoding.UTF8.GetString(packet, 4, 4);
if (protocolName != "MQTT")
{
await SendConnAck(session, 0x01); // 返回不支持协议版本
return;
}
// 检查协议级别 (偏移量9)
var protocolLevel = packet[8];
if (protocolLevel != 0x04)
{ // MQTT 3.1.1的协议级别是4,MQTT 5.0.0的协议级别是5
await SendConnAck(session, 0x01);
return;
}
// 处理客户端ID
var clientIdLength = (packet[12] << 8) | packet[13];
session.ClientId = Encoding.UTF8.GetString(packet, 14, clientIdLength);
// 检查重复ClientID
if (_clientSessions.ContainsKey(session.ClientId))
{
await SendConnAck(session, 0x02); // 0x02表示标识符被占用
return;
}
// 添加到会话字典
if (!_clientSessions.TryAdd(session.ClientId, session))
{
await SendConnAck(session, 0x01); // 0x01表示服务器不可用
return;
}
session.Status = MqttConnectionStatus.Connected;
// 必须返回CONNACK (Accepted)
await SendConnAck(session, 0x00);
Debug.WriteLine($"客户端已连接: {session.ClientId}");
}
private async Task SendConnAck(ClientSession session, byte returnCode)
{
//CONNACK格式:
//Byte1: 固定头 (0x20)
//Byte2: 剩余长度 (0x02)
//Byte3: 保留位 (0x00)
//Byte4: Return Code
var connAck = new byte[] {
0x20, // CONNACK类型
0x02, // 剩余长度
0x00, // 保留位
returnCode // 0=成功, 1-5=错误码
};
await SendDataAsync(session, connAck);
}
/// <summary>
/// 处理PUBLISH报文
/// </summary>
private async Task HandlePublishAsync(ClientSession session, byte[] packet)
{
// 解析PUBLISH报文
int offset = 1; // 跳过固定头
// 读取剩余长度
int remainingLength = 0;
int multiplier = 1;
byte digit;
do
{
digit = packet[offset++];
remainingLength += (digit & 127) * multiplier;
multiplier *= 128;
} while ((digit & 128) != 0 && offset < 5);
// 读取主题长度(2字节)
if (offset + 2 > packet.Length)
throw new ArgumentException("Invalid topic length");
int topicLength = (packet[offset] << 8) | packet[offset + 1];
offset += 2;
// 读取主题内容
if (offset + topicLength > packet.Length)
throw new ArgumentException("Topic exceeds packet length");
string topic = Encoding.UTF8.GetString(packet, offset, topicLength);
offset += topicLength;
// 读取消息内容
var payloadLength = remainingLength - offset + 2; // 即packet.Length - offset
var payload = new byte[payloadLength];
Array.Copy(packet, offset, payload, 0, payloadLength);
var qosLevel = (byte)((packet[0] & 0x06) >> 1);
var retain = (packet[0] & 0x01) != 0;
var message = new MqttMessage
{
Topic = topic,
Payload = payload,
QosLevel = qosLevel,
Retain = retain
};
// 处理保留消息
if (retain)
{
_retainedMessages.Add(message);
}
// 路由消息给订阅者
_workerThreadPool.QueueUserWorkItem(async () =>
{
await RouteMessageAsync(message).ConfigureAwait(false);
});
}
/// <summary>
/// 路由消息给订阅者
/// </summary>
private async Task RouteMessageAsync(MqttMessage message)
{
// 获取所有匹配的订阅者
var subscribers = _topicSubscriptions
.Where(kv => TopicMatches(kv.Key, message.Topic))
.SelectMany(kv => kv.Value)
.Distinct()
.ToList();
// 并行发送
var sendTasks = subscribers.Select(async sub =>
{
try
{
await SendPublishAsync(sub, message);
}
catch (Exception ex)
{
Debug.WriteLine($"发送到{sub.ClientId}失败: {ex.Message}");
DisconnectClient(sub);
}
});
await Task.WhenAll(sendTasks).ConfigureAwait(false);
}
/// <summary>
/// 检查主题是否匹配
/// </summary>
private bool TopicMatches(string subscription, string topic)
{
return subscription == topic; // 要支持通配符,懒得搞了
}
/// <summary>
/// 发送PUBLISH报文给客户端
/// </summary>
private async Task SendPublishAsync(ClientSession session, MqttMessage message)
{
try
{
var topicBytes = Encoding.UTF8.GetBytes(message.Topic);
var variableHeaderLength = 2 + topicBytes.Length;
var remainingLength = variableHeaderLength + message.Payload.Length;
var packet = new List<byte>();
// 固定头
byte fixedHeader = (byte)((byte)ControlPacketType.PUBLISH << 4);
fixedHeader |= (byte)(message.QosLevel << 1);
if (message.Retain) fixedHeader |= 0x01;
packet.Add(fixedHeader);
// 剩余长度
packet.AddRange(EncodeRemainingLength(remainingLength));
// 可变头
packet.Add((byte)(topicBytes.Length >> 8));
packet.Add((byte)topicBytes.Length);
packet.AddRange(topicBytes);
// 有效载荷
packet.AddRange(message.Payload);
// 确保线程安全发送
await session.SendLock.WaitAsync(_cts.Token).ConfigureAwait(false);
try
{
await session.NetworkStream.WriteAsync(packet.ToArray(), 0, packet.Count, _cts.Token).ConfigureAwait(false);
}
finally
{
session.SendLock.Release();
}
}
catch (Exception ex)
{
Debug.WriteLine($"Send publish to {session.ClientId} error: {ex.Message}");
}
}
/// <summary>
/// 处理SUBSCRIBE报文
/// </summary>
private async Task HandleSubscribeAsync(ClientSession session, byte[] packet)
{
// 解析SUBSCRIBE报文
int offset = 1; // 跳过固定头
// 读取剩余长度
int remainingLength = packet[offset++];
// 读取Packet Identifier(2字节)
int packetId = (packet[offset] << 8) | packet[offset + 1];
offset += 2;
// 读取订阅列表
List<string> topics = new List<string>();
List<byte> returnCodes = new List<byte>();
while (offset < packet.Length)
{
// 读取主题长度
int topicLength = (packet[offset] << 8) | packet[offset + 1];
offset += 2;
// 读取主题
string topic = Encoding.UTF8.GetString(packet, offset, topicLength);
offset += topicLength;
// 读取请求的QoS级别(只取后2位)
byte requestedQos = (byte)(packet[offset] & 0x03);
offset++;
// 检查主题有效性
if (string.IsNullOrEmpty(topic))
{
returnCodes.Add(0x80); // 0x80表示订阅失败
continue;
}
topics.Add(topic);
// 添加到主题订阅字典
_topicSubscriptions.AddOrUpdate(
topic,
new List<ClientSession> { session },
(_, list) => { lock (list) { list.Add(session); } return list; });
session.SubscribedTopics.Add(topic); // 添加到会话订阅列表
returnCodes.Add(requestedQos); // 返回实际授予的QoS
}
// 构建SUBACK报文
List<byte> subAckPacket = new List<byte>
{
0x90, // SUBACK固定头
(byte)(2 + returnCodes.Count), // 剩余长度
(byte)(packetId >> 8), // Packet Identifier MSB
(byte)(packetId & 0xFF) // Packet Identifier LSB
};
subAckPacket.AddRange(returnCodes); // 添加返回码列表
await SendDataAsync(session, subAckPacket.ToArray()).ConfigureAwait(false);
// 发送保留消息
_workerThreadPool.QueueUserWorkItem(async () =>
{
await SendRetainedMessagesAsync(session, topics).ConfigureAwait(false);
});
}
/// <summary>
/// 发送保留消息给新订阅者
/// </summary>
private async Task SendRetainedMessagesAsync(ClientSession session, IEnumerable<string> topics)
{
var retainedMessages = _retainedMessages
.Where(msg => topics.Contains(msg.Topic))
.ToList();
foreach (var msg in retainedMessages)
{
await SendPublishAsync(session, msg).ConfigureAwait(false);
}
}
/// <summary>
/// 发送PINGRESP响应
/// </summary>
private async Task SendPingRespAsync(ClientSession session)
{
var pingResp = new byte[] { 0xD0, 0x00 };
await SendDataAsync(session, pingResp).ConfigureAwait(false);
}
/// <summary>
/// 通用数据发送方法
/// </summary>
private async Task SendDataAsync(ClientSession session, byte[] data)
{
await session.SendLock.WaitAsync(_cts.Token).ConfigureAwait(false);
try
{
await session.NetworkStream.WriteAsync(data, 0, data.Length, _cts.Token).ConfigureAwait(false);
}
finally
{
session.SendLock.Release();
}
}
/// <summary>
/// 断开客户端连接
/// </summary>
private void DisconnectClient(ClientSession session)
{
if (session.Status == MqttConnectionStatus.Disconnected)
{
return;
}
Debug.WriteLine($"客户端断开: {session.ClientId}");
// 1. 从会话字典移除
if (session.ClientId != null)
{
_clientSessions.TryRemove(session.ClientId, out _);
}
// 2. 从所有订阅中移除
foreach (var topic in session.SubscribedTopics)
{
if (_topicSubscriptions.TryGetValue(topic, out var subscribers))
{
lock (subscribers) // 确保线程安全修改列表
{
subscribers.Remove(session);
}
}
}
// 3. 释放资源
session.Status = MqttConnectionStatus.Disconnected;
session.Dispose();
Debug.WriteLine($"Client disconnected: {session.ClientId}");
}
/// <summary>
/// 清理不活跃会话
/// </summary>
private async Task CleanInactiveSessionsAsync()
{
while (!_cts.IsCancellationRequested)
{
await Task.Delay(KeepAliveCheckInterval, _cts.Token).ConfigureAwait(false);
var now = DateTime.UtcNow;
var inactiveSessions = _clientSessions.Values
.Where(s => now - s.LastActivityTime > TimeSpan.FromMinutes(5))
.ToList();
foreach (var session in inactiveSessions)
{
DisconnectClient(session);
}
}
}
/// <summary>
/// 编码剩余长度字段
/// </summary>
private static IEnumerable<byte> EncodeRemainingLength(int length)
{
var bytes = new List<byte>();
do
{
byte digit = (byte)(length % 128);
length /= 128;
if (length > 0) digit |= 0x80;
bytes.Add(digit);
} while (length > 0);
return bytes;
}
public void Dispose()
{
if (_disposed) return;
_disposed = true;
_cts.Cancel();
// 断开所有客户端
foreach (var session in _clientSessions.Values)
{
DisconnectClient(session);
}
_listener.Stop();
_workerThreadPool.Dispose();
_cts.Dispose();
Debug.WriteLine("MQTT Broker stopped");
}
}
}
MQTT是基于TCP的一个应用层协议,所以很像写一个TCP服务器。无非是接收客户端连接,然后给每个客户端分配一个线程处理消息的收发。
2.3 线程池
为了方便分配和管理线程,再写个线程池。
namespace 简单MqttBroker
{
/// <summary>
/// 自定义线程池实现
/// </summary>
public class ThreadPool : IDisposable
{
private readonly BlockingCollection<(Action WorkItem, ManualResetEventSlim CompletionEvent)> _workQueue = new();
private readonly List<Thread> _workerThreads;
private readonly CancellationTokenSource _cts = new();
private volatile bool _disposed;
private long _completedCount;
public int ActiveThreads => _workerThreads.Count(t => t.IsAlive);
public long CompletedCount => Interlocked.Read(ref _completedCount);
public int MaxThreads { get; }
public ThreadPool(int maxThreads)
{
MaxThreads = maxThreads > 0 ? maxThreads : throw new ArgumentOutOfRangeException(nameof(maxThreads));
_workerThreads = new List<Thread>(maxThreads);
// 预热工作线程
for (int i = 0; i < maxThreads; i++)
{
var thread = new Thread(WorkerThreadProc)
{
IsBackground = true,
Name = $"NativeThreadPool Worker #{i}"
};
thread.Start();
_workerThreads.Add(thread);
}
}
private void WorkerThreadProc()
{
while (!_disposed && !_cts.IsCancellationRequested)
{
try
{
var (workItem, completionEvent) = _workQueue.Take(_cts.Token);
try
{
workItem();
Interlocked.Increment(ref _completedCount);
}
finally
{
completionEvent?.Set();
}
}
catch (OperationCanceledException) { /* 正常退出 */ }
catch (Exception ex)
{
Debug.WriteLine($"Worker thread error: {ex.Message}");
}
}
}
public bool QueueUserWorkItem(WaitCallback callBack, object? state = null)
{
if (_disposed)
{
throw new ObjectDisposedException(nameof(ThreadPool));
}
ManualResetEventSlim mre = new ManualResetEventSlim(false);
_workQueue.Add((() => callBack(state), mre));
return true;
}
public bool QueueUserWorkItem(Action workItem)
{
if (_disposed) throw new ObjectDisposedException(nameof(ThreadPool));
var mre = new ManualResetEventSlim(false);
_workQueue.Add((workItem, mre));
return true;
}
public void GetAvailableThreads(out int workerThreads, out int completionPortThreads)
{
workerThreads = MaxThreads - ActiveThreads;
completionPortThreads = 0; // 原生线程池不区分I/O线程
}
public void Dispose()
{
if (_disposed)
{
return;
}
_disposed = true;
_cts.Cancel();
_workQueue.CompleteAdding();
foreach (var thread in _workerThreads)
{
if (thread.IsAlive)
{
thread.Join(500); // 等待线程退出
}
}
_workQueue.Dispose();
_cts.Dispose();
}
}
}
3,用起来
3.1 Broker
随便创建一个项目,比如winform项目,引用上面写的简单MqttBroker,把Broker跑起来。
public partial class Form1 : Form
{
//实现的MQTT协议版本是3.1.1
MqttBroker broker = new MqttBroker(1883);
public Form1()
{
InitializeComponent();
}
private void 开始_Click(object sender, EventArgs e)
{
broker.Start();
开始.Enabled = false;
}
}
3.2 Client
市面上Client很多,我选了两种来测试,一种是现成的客户端MQTTX,另一种是引用MQTTnet自己写一个。然后让几个客户端订阅同一个主题相互通信。
3.2.1 MQTTX
打开MQTTX,新建连接,然后订阅主题abc。


3.2.2 MQTTnet
新建一个winform项目,nuget安装MQTTnet
public partial class Form1 : Form
{
IMqttClient mqttClient = new MqttFactory().CreateMqttClient();
IMqttClient mqttClient2 = new MqttFactory().CreateMqttClient();
public Form1()
{
InitializeComponent();
}
private async void 开始Client1_Click(object sender, EventArgs e)
{
try
{
开始Client1.Enabled = false;
MqttClientOptionsBuilder optionbuilder = new MqttClientOptionsBuilder()
.WithClientId("ccclient1")
.WithTcpServer("127.0.0.1", 1883)
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311);
MqttClientConnectResult ret = await mqttClient.ConnectAsync(optionbuilder.Build());
SetString($"Client1连接:{ret.ResultCode}\r\n");
}
catch(Exception ex)
{
SetString($"Client1连接:{ex.Message}\r\n");
}
finally
{
开始Client1.Enabled = true;
}
}
private async void Client1发布_Click(object sender, EventArgs e)
{
if (mqttClient.IsConnected)
{
MqttClientPublishResult ret = await mqttClient.PublishAsync(new MqttApplicationMessage()
{
Topic = "abc",
PayloadSegment = Encoding.UTF8.GetBytes($"我是Client1,现在时间是{DateTime.Now}")
}, CancellationToken.None);
SetString($"Client1发布:{ret.IsSuccess}\r\n");
}
}
private async void 开始Client2_Click(object sender, EventArgs e)
{
try
{
开始Client2.Enabled = false;
MqttClientOptionsBuilder optionbuilder = new MqttClientOptionsBuilder()
.WithClientId("ccclient2")
.WithTcpServer("127.0.0.1", 1883)
.WithProtocolVersion(MQTTnet.Formatter.MqttProtocolVersion.V311);
MqttClientConnectResult ret = await mqttClient2.ConnectAsync(optionbuilder.Build());
SetString($"Client2连接:{ret.ResultCode}\r\n");
}
catch (Exception ex)
{
SetString($"Client2连接:{ex.Message}\r\n");
}
finally
{
开始Client2.Enabled = true;
}
}
private async void Client2订阅_Click(object sender, EventArgs e)
{
if (mqttClient2.IsConnected)
{
MqttClientSubscribeResult ret = await mqttClient2.SubscribeAsync(new MqttClientSubscribeOptions()
{
TopicFilters = new List<MqttTopicFilter>
{
new MqttTopicFilter()
{
Topic = "abc"
}
}
});
SetString($"Client2订阅:{string.Join(',', ret.Items.Select(i => i.ResultCode))}\r\n");
mqttClient2.ApplicationMessageReceivedAsync += MqttClient2_ApplicationMessageReceivedAsync;
}
}
private async Task MqttClient2_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
{
var msg = arg.ApplicationMessage;
var topic = msg.Topic;
var payload = msg.ConvertPayloadToString();
SetString($"收到消息 clientId:{arg.ClientId}, topic:{topic}, payload:{payload}\r\n");
}
private void SetString(string str)
{
if (this.InvokeRequired)
{
this.Invoke(new Action(() => textBox1.AppendText(str)));
}
else
{
textBox1.AppendText(str);
}
}
}
跑起来,然后开始Client1,开始Client2,Client2订阅。

3.3 收发消息
目前的状态共有3个客户端连接到Broker,1号是MQTTX(mqttx_d6bcd3f7),2号是Client1(ccclient1),3号是Client2(ccclient2)。其中mqttx_d6bcd3f7和ccclient2订阅了主题abc。然后分别用mqttx_d6bcd3f7和ccclient1向主题abc发送消息,mqttx_d6bcd3f7和ccclient2可以收到消息并打印出来。

没毛病,符合预期。
如遇问题,可参考MQTT 3.1.1协议规范 https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

浙公网安备 33010602011771号