在Unity3d2021.3.35中实现MQTT异步客户端

支持Windows和Android平台,可自动重连。

1.下载MQTTnet,解压

2.将lib/net48/MQTTnet.dll放入 Unity 项目 Assets/Plugins 目录

3.在场景中创建物体,将脚本MqttMgr.cs、UnityMainThreadDispatcher.cs、MqttTest.cs添加到该物体。

cs 复制代码
using System;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;
using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Protocol;

public class MqttMgr : MonoBehaviour
{
    public static MqttMgr Instance { get; private set; }

    [Header("MQTT 配置")]
    public string broker = "broker.hivemq.com";
    public int port = 1883;
    public string clientIdPrefix = "UnityClient_";
    public string username = "";
    public string password = "";

    [Header("自动重连")]
    public bool autoReconnect = true;
    public int reconnectInterval = 3000;

    public event Action OnConnected;
    public event Action<string> OnDisconnected;
    public event Action<string, string> OnMessageReceived;
    public event Action<string> OnLog;

    // 【新增】MQTT连接状态属性,外部直接调用:MqttMgr.Instance.isMqttConnected
    public bool isMqttConnected => _mqttClient != null && _mqttClient.IsConnected;
    /// <summary>
    /// 是否已初始化
    /// </summary>
    public static bool IsInitialized => Instance != null;

    private IMqttClient _mqttClient;
    private CancellationTokenSource _cts;
    private string _clientId;
    private bool _isConnecting;
    private bool _manualDisconnect;

    private const string MQTT_TOPIC = "yiyue/lanyi";
    private const string MQTT_PUBLISH = "lanyi/yiyue";
    public string MqttTopic => MQTT_TOPIC;

    private  void Awake()
    {
        if (Instance != null) { Destroy(gameObject); return; }
        Instance = this;
        DontDestroyOnLoad(gameObject);
        _clientId = clientIdPrefix + Guid.NewGuid().ToString("N").Substring(0, 8);
    }

    public async Task ConnectAsync()
    {
        if (_isConnecting) return;

        try
        {
            _manualDisconnect = false;
            _isConnecting = true;

            if (_cts != null)
            {
                _cts.Cancel();
                _cts.Dispose();
            }
            _cts = new CancellationTokenSource();

            if (_mqttClient != null)
            {
                _mqttClient.ConnectedAsync -= OnClientConnected;
                _mqttClient.DisconnectedAsync -= OnClientDisconnected;
                _mqttClient.ApplicationMessageReceivedAsync -= OnApplicationMessageReceived;
                _mqttClient.Dispose();
                _mqttClient = null;
            }

            var factory = new MqttFactory();
            _mqttClient = factory.CreateMqttClient();

            var options = new MqttClientOptionsBuilder()
                .WithTcpServer(broker, port)
                .WithClientId(_clientId)
                .WithCleanSession()
                .WithKeepAlivePeriod(TimeSpan.FromSeconds(10))
                .Build();

            _mqttClient.ConnectedAsync += OnClientConnected;
            _mqttClient.DisconnectedAsync += OnClientDisconnected;
            _mqttClient.ApplicationMessageReceivedAsync += OnApplicationMessageReceived;

            // 5秒超时保护,防止断网卡死
            var connectTask = _mqttClient.ConnectAsync(options, _cts.Token);
            var timeoutTask = Task.Delay(5000, _cts.Token);
            await Task.WhenAny(connectTask, timeoutTask);

            if (!connectTask.IsCompletedSuccessfully)
            {
                _cts.Cancel();
                Log("连接超时,继续重试...");
            }
        }
        catch (Exception ex)
        {
            Log($"连接异常:{ex.Message}");
        }
        finally
        {
            _isConnecting = false;

            // 失败就继续重连 → 无限循环
            if (!_manualDisconnect && autoReconnect && (_mqttClient == null || !_mqttClient.IsConnected))
            {
                _ = TryReconnectAsync();
            }
        }
    }

    public void SetBrokerAddress(string brokerIp)
    {
        if (string.IsNullOrWhiteSpace(brokerIp))
            return;
        broker = brokerIp;
    }

    public void SetBrokerPort(string bport)
    {
        if (string.IsNullOrWhiteSpace(bport))
            return;
        int.TryParse(bport, out port);
    }

    private Task OnClientConnected(MqttClientConnectedEventArgs args)
    {
        OnMainThread(() =>
        {
            Log("✅ MQTT 已连接");
            OnConnected?.Invoke();
            if(OnConnected != null)
                Log("OnConnected not null");
            else
                Log("OnConnected is null");
        });
        return Task.CompletedTask;
    }

    private async Task OnClientDisconnected(MqttClientDisconnectedEventArgs args)
    {
        OnMainThread(() =>
        {
            Log($"❌ 断开:{args.Reason}");
            OnDisconnected?.Invoke(args.Reason.ToString());
        });

        if (_manualDisconnect || !autoReconnect)
            return;

        await TryReconnectAsync();
    }

    private Task OnApplicationMessageReceived(MqttApplicationMessageReceivedEventArgs args)
    {
        var topic = args.ApplicationMessage.Topic;
        var payload = Encoding.UTF8.GetString(args.ApplicationMessage.PayloadSegment);
        OnMainThread(() => OnMessageReceived?.Invoke(topic, payload));
        return Task.CompletedTask;
    }

    // 无限重连核心
    private async Task TryReconnectAsync()
    {
        if (_isConnecting) return;

        Log($"⏳ {reconnectInterval / 1000} 秒后自动重试...");
        await Task.Delay(reconnectInterval);

        if (!_manualDisconnect && autoReconnect)
        {
            await ConnectAsync();
        }
    }

    public async Task DisconnectAsync()
    {
        _manualDisconnect = true;
        try
        {
            if (_cts != null)
            {
                _cts.Cancel();
                _cts.Dispose();
                _cts = null;
            }
            if (_mqttClient != null)
                await _mqttClient.DisconnectAsync();
        }
        catch { }
    }

    public async Task SubscribeAsync(string topic, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtMostOnce)
    {
        if (_mqttClient == null || !_mqttClient.IsConnected) { Log("未连接"); return; }
        try { await _mqttClient.SubscribeAsync(topic, qos); Log($"已订阅:{topic}"); }
        catch (Exception ex) { Log($"订阅失败:{ex.Message}"); }
    }

    public async Task PublishAsync(string topic, string payload, MqttQualityOfServiceLevel qos = MqttQualityOfServiceLevel.AtMostOnce, bool retain = false)
    {
        if (_mqttClient == null || !_mqttClient.IsConnected) { Log("未连接"); return; }
        try
        {
            var msg = new MqttApplicationMessageBuilder()
                .WithTopic(topic).WithPayload(payload)
                .WithQualityOfServiceLevel(qos).WithRetainFlag(retain).Build();
            await _mqttClient.PublishAsync(msg);
            Log($"发布 [{topic}]:{payload}");
        }
        catch (Exception ex) { Log($"发布失败:{ex.Message}"); }
    }

    public async Task PublishAsync(string payload)
    {
        await MqttMgr.Instance.PublishAsync("lanyi/yiyue", payload);
    }

    private void Log(string msg) => OnMainThread(() => { Debug.Log($"[MQTT] {msg}"); OnLog?.Invoke(msg); });
    private void OnMainThread(Action act) => UnityMainThreadDispatcher.Instance.Enqueue(act);

    private async void OnDestroy() => await DisconnectAsync();
}
cs 复制代码
using System;
using System.Collections.Generic;
using UnityEngine;

public class UnityMainThreadDispatcher : MonoBehaviour
{
    public static UnityMainThreadDispatcher Instance { get; private set; }
    private readonly Queue<Action> _queue = new Queue<Action>();

    private void Awake()
    {
        if (Instance != null)
        {
            Destroy(gameObject);
            return;
        }
        Instance = this;
        DontDestroyOnLoad(gameObject);
    }

    public void Enqueue(Action action)
    {
        lock (_queue)
        {
            _queue.Enqueue(action);
        }
    }

    private void Update()
    {
        lock (_queue)
        {
            while (_queue.Count > 0)
                _queue.Dequeue()?.Invoke();
        }
    }
}
cs 复制代码
using UnityEngine;
using UnityEngine.UI;

public class MqttTest : MonoBehaviour
{
    public Text textOutput;
    public Button btnClear;
    async void Start()
    {
        btnClear.onClick.AddListener(()=>
        {
            textOutput.text = "";
        });
       

        // 订阅
        //await MqttMgr.Instance.SubscribeAsync("yiyue/lanyi");
        // 2. 连接成功后 → 自动订阅
        MqttMgr.Instance.OnConnected += async () =>
        {
            await MqttMgr.Instance.SubscribeAsync("yiyue/lanyi");
            Instance_OnLog($"订阅 yiyue/lanyi");
        };
        // 连接
        await MqttMgr.Instance.ConnectAsync();
        // 发布
        await MqttMgr.Instance.PublishAsync("lanyi/yiyue", "Hello from Unity!");

        // 监听消息
        MqttMgr.Instance.OnMessageReceived += (topic, msg) =>
        {
            Debug.Log($"收到 {topic}: {msg}");
            textOutput.text += $"收到 {topic}: {msg}"+"\r\n";
        };

        MqttMgr.Instance.OnLog += Instance_OnLog;
    }

    private void Instance_OnLog(string obj)
    {
        textOutput.text += $"{obj}" + "\r\n";
    }
}
相关推荐
海海不瞌睡(捏捏王子)4 小时前
Unity YAML
unity·游戏引擎
海海不瞌睡(捏捏王子)6 小时前
Unity A*寻路算法
算法·unity
weixin_423995006 小时前
unity 虚拟数字人-接讯飞虚拟人
unity·游戏引擎
小贺儿开发7 小时前
Unity3D 家居视频遥控效果演示
unity·udp·人机交互·网络通信·winform·远程·photon
墨染倾城殇8 小时前
Realtek RTL8720DN Wi-Fi4 MQTT 智能硬件通信方案实现
mqtt·智能家居·工业物联·智能硬件通信·realtek
mxwin8 小时前
Unity URP 阴影映射 深度纹理、阴影采样与分辨率控制的深度解析
unity·游戏引擎·shader·着色器
amadeusCristina10 小时前
Unity中生命周期调用时机
unity·游戏引擎
C蔡博士1 天前
Unity2D物理系统-从入门到实战优化
unity·游戏引擎·rigidbody2d
mxwin1 天前
Unity Shader 顶点动画:在顶点着色器中实现风吹草动、河流波动、布料模拟
unity·游戏引擎·shader·着色器