MQTT之NanoSDK-Windows篇(二)
一、开篇
上文MQTT之NanoSDK-Windows篇(一) - !>Mon<! - 博客园,简单使用NanoSDK示例与NanoMQ进行发布和订阅的测试。本篇主要内容是关于基于NanoSDK封装的库,在C#端应用,使用也非常简单。可以前往uncle-fang-zh/MQ_Client_CSharp: NanoSDK开发包实例,开发语言C#,下载示例配合服务端进行简单的发布和订阅测试。注意当前编译的dll,指定在x86平台使用并先开启服务端程序。
二、演示

三、主要流程
- 连接本地 Broker(ClientID: `csharp_client`)
- 订阅主题 `test/topic`(QoS 1)
- 连续发布 10 条消息到主题 `world`(QoS 1),间隔 1 秒
- 按任意键退出并断开连接
四、相关事件
- `Connected` / `Disconnected` - 连接状态变化
- `MessageReceived` - 收到消息
- `MessagePublished` - 发布完成回调
- `Subscribed` - 订阅确认回调
五、开发调用
将FileName.cs和NativeMethods.cs复制到工程;将WinNanoSDK.dll、nng.dll复制到可执行目录下
using System; using System.Runtime.InteropServices; using System.Text; namespace NanoMQTT { /// <summary> /// MQTT 客户端事件参数 /// </summary> public class MQTTMessageEventArgs : EventArgs { public string Topic { get; set; } public byte[] Payload { get; set; } public byte Qos { get; set; } public string PayloadAsString => Encoding.UTF8.GetString(Payload); } public class MQTTConnectEventArgs : EventArgs { public int Reason { get; set; } public bool Success => Reason == 0; } public class MQTTDisconnectEventArgs : EventArgs { public int Reason { get; set; } } public class MQTTPublishEventArgs : EventArgs { public uint MessageId { get; set; } public int Result { get; set; } public bool Success => Result == 0; } public class MQTTSubscribeEventArgs : EventArgs { public byte[] ReasonCodes { get; set; } public bool Success => ReasonCodes != null && ReasonCodes.Length > 0 && ReasonCodes[0] < 0x80; } /// <summary> /// NanoSDK MQTT 客户端 (.NET 封装) /// </summary> public class NanoMQTTClient : IDisposable { private IntPtr _handle; private bool _disposed = false; private GCHandle _gcHandle; // 关键:保存委托引用,防止 GC 回收 private NativeMethods.ConnectCallback _connectCallback; private NativeMethods.DisconnectCallback _disconnectCallback; private NativeMethods.MessageCallback _messageCallback; private NativeMethods.PublishCallback _publishCallback; private NativeMethods.SubscribeCallback _subscribeCallback; public event EventHandler<MQTTConnectEventArgs> Connected; public event EventHandler<MQTTDisconnectEventArgs> Disconnected; public event EventHandler<MQTTMessageEventArgs> MessageReceived; public event EventHandler<MQTTPublishEventArgs> MessagePublished; public event EventHandler<MQTTSubscribeEventArgs> Subscribed; public bool IsConnected { get; private set; } public NanoMQTTClient() { _handle = NativeMethods.mqtt_client_create(); if (_handle == IntPtr.Zero) throw new Exception("Failed to create MQTT client"); // 分配 GCHandle 防止对象被回收 _gcHandle = GCHandle.Alloc(this); // 创建委托实例并保存到字段 _connectCallback = OnConnectCallback; _disconnectCallback = OnDisconnectCallback; _messageCallback = OnMessageCallback; _publishCallback = OnPublishCallback; _subscribeCallback = OnSubscribeCallback; RegisterCallbacks(); } private void RegisterCallbacks() { IntPtr ptr = GCHandle.ToIntPtr(_gcHandle); // 传递保存的委托,而不是直接传递方法 NativeMethods.mqtt_client_set_connect_callback(_handle, _connectCallback, ptr); NativeMethods.mqtt_client_set_disconnect_callback(_handle, _disconnectCallback, ptr); NativeMethods.mqtt_client_set_message_callback(_handle, _messageCallback, ptr); NativeMethods.mqtt_client_set_publish_callback(_handle, _publishCallback, ptr); NativeMethods.mqtt_client_set_subscribe_callback(_handle, _subscribeCallback, ptr); } // ==================== 静态回调方法 ==================== // 所有回调都添加 try-catch 防止异常崩溃 private static void OnConnectCallback(int reason, IntPtr userData) { try { if (userData == IntPtr.Zero) return; var gcHandle = GCHandle.FromIntPtr(userData); var client = gcHandle.Target as NanoMQTTClient; if (client == null || client._disposed) return; client.IsConnected = reason == 0; client.Connected?.Invoke(client, new MQTTConnectEventArgs { Reason = reason }); } catch (Exception ex) { Console.WriteLine($"[ConnectCallback] ERROR: {ex.Message}"); } } private static void OnDisconnectCallback(int reason, IntPtr userData) { try { if (userData == IntPtr.Zero) return; var gcHandle = GCHandle.FromIntPtr(userData); var client = gcHandle.Target as NanoMQTTClient; if (client == null || client._disposed) return; client.IsConnected = false; client.Disconnected?.Invoke(client, new MQTTDisconnectEventArgs { Reason = reason }); } catch (Exception ex) { Console.WriteLine($"[DisconnectCallback] ERROR: {ex.Message}"); } } private static void OnMessageCallback(string topic, IntPtr payload, uint payloadLen, byte qos, IntPtr userData) { try { if (userData == IntPtr.Zero || payloadLen == 0) return; var gcHandle = GCHandle.FromIntPtr(userData); var client = gcHandle.Target as NanoMQTTClient; if (client == null || client._disposed) return; byte[] data = new byte[payloadLen]; Marshal.Copy(payload, data, 0, (int)payloadLen); client.MessageReceived?.Invoke(client, new MQTTMessageEventArgs { Topic = topic ?? string.Empty, Payload = data, Qos = qos }); } catch (Exception ex) { Console.WriteLine($"[MessageCallback] ERROR: {ex.Message}"); } } private static void OnPublishCallback(uint msgId, int result, IntPtr userData) { try { if (userData == IntPtr.Zero) return; var gcHandle = GCHandle.FromIntPtr(userData); var client = gcHandle.Target as NanoMQTTClient; if (client == null || client._disposed) return; client.MessagePublished?.Invoke(client, new MQTTPublishEventArgs { MessageId = msgId, Result = result }); } catch (Exception ex) { Console.WriteLine($"[PublishCallback] ERROR: {ex.Message}"); } } private static void OnSubscribeCallback(IntPtr reasonCodes, uint count, IntPtr userData) { try { if (userData == IntPtr.Zero || count == 0) return; var gcHandle = GCHandle.FromIntPtr(userData); var client = gcHandle.Target as NanoMQTTClient; if (client == null || client._disposed) return; byte[] codes = new byte[count]; Marshal.Copy(reasonCodes, codes, 0, (int)count); client.Subscribed?.Invoke(client, new MQTTSubscribeEventArgs { ReasonCodes = codes }); } catch (Exception ex) { Console.WriteLine($"[SubscribeCallback] ERROR: {ex.Message}"); } } // ==================== 公共 API ==================== public int Connect(string url, string clientId = null, string username = null, string password = null, ushort keepAlive = 60, bool cleanSession = true, byte protocolVersion = 4) { return NativeMethods.mqtt_client_connect( _handle, url, clientId, username, password, keepAlive, cleanSession, protocolVersion); } public int Disconnect(int timeoutMs = 5000) { return NativeMethods.mqtt_client_disconnect(_handle, timeoutMs); } public int Publish(string topic, byte[] payload, byte qos = 0, bool retain = false) { return NativeMethods.mqtt_client_publish( _handle, topic, payload, (uint)payload.Length, qos, retain); } public int Publish(string topic, string message, byte qos = 0, bool retain = false) { byte[] payload = Encoding.UTF8.GetBytes(message); return Publish(topic, payload, qos, retain); } public int Subscribe(string topic, byte qos = 0) { return NativeMethods.mqtt_client_subscribe(_handle, topic, qos); } public int Unsubscribe(string topic) { return NativeMethods.mqtt_client_unsubscribe(_handle, topic); } public void SetWill(string topic, string message, byte qos = 0, bool retain = false) { NativeMethods.mqtt_client_set_will(_handle, topic, message, qos, retain); } public string GetLastError() { return NativeMethods.mqtt_client_last_error(_handle); } // ==================== Dispose ==================== public void Dispose() { if (_disposed) return; try { if (_handle != IntPtr.Zero) { Disconnect(); NativeMethods.mqtt_client_destroy(_handle); _handle = IntPtr.Zero; } } catch { } if (_gcHandle.IsAllocated) _gcHandle.Free(); _disposed = true; GC.SuppressFinalize(this); } ~NanoMQTTClient() { Dispose(); } } }
using System; using System.Runtime.InteropServices; namespace NanoMQTT { internal static class NativeMethods { private const string DllName = "WinNanoSDK.dll"; // 委托定义 [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void ConnectCallback(int reason, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void DisconnectCallback(int reason, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void MessageCallback( [MarshalAs(UnmanagedType.LPStr)] string topic, IntPtr payload, uint payloadLen, byte qos, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void PublishCallback(uint msgId, int result, IntPtr userData); [UnmanagedFunctionPointer(CallingConvention.Cdecl)] public delegate void SubscribeCallback(IntPtr reasonCodes, uint count, IntPtr userData); // 导入函数 [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern IntPtr mqtt_client_create(); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_destroy(IntPtr handle); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern int mqtt_client_connect( IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string url, [MarshalAs(UnmanagedType.LPStr)] string clientId, [MarshalAs(UnmanagedType.LPStr)] string username, [MarshalAs(UnmanagedType.LPStr)] string password, ushort keepAlive, bool cleanSession, byte protocolVersion); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern int mqtt_client_disconnect(IntPtr handle, int timeoutMs); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern int mqtt_client_publish( IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string topic, byte[] payload, uint payloadLen, byte qos, bool retain); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern int mqtt_client_subscribe( IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string topic, byte qos); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern int mqtt_client_unsubscribe( IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string topic); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_will( IntPtr handle, [MarshalAs(UnmanagedType.LPStr)] string topic, [MarshalAs(UnmanagedType.LPStr)] string message, byte qos, bool retain); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_connect_callback( IntPtr handle, ConnectCallback cb, IntPtr userData); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_disconnect_callback( IntPtr handle, DisconnectCallback cb, IntPtr userData); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_message_callback( IntPtr handle, MessageCallback cb, IntPtr userData); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_publish_callback( IntPtr handle, PublishCallback cb, IntPtr userData); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] public static extern void mqtt_client_set_subscribe_callback( IntPtr handle, SubscribeCallback cb, IntPtr userData); [DllImport(DllName, CallingConvention = CallingConvention.Cdecl , CharSet =CharSet.Unicode)] [return: MarshalAs(UnmanagedType.LPStr)] public static extern string mqtt_client_last_error(IntPtr handle); } }


浙公网安备 33010602011771号