Unity3d使用MQTT异步连接服务端

1.下载MQTTnet.4.3.7.1057.nupkg

2.我用的Unity3d 2021.3.35,对应的 .NET Framework 版本是 4.8,需要将lib/net48下的MQTTnet.dll放到Assets/Plugins下

3.Project Settings一般不用改

4.代码

MqttMgr.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 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 && PubMethod.ReconnectLanYiMgr && (_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 || !PubMethod.ReconnectLanYiMgr)
            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 && PubMethod.ReconnectLanYiMgr)
        {
            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();
}

UnityMainThreadDispatcher.cs

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 System;
using System.Collections;
using System.Collections.Generic;
using System.Linq;
using System.Threading.Tasks;
using UnityEngine;
using UnityEngine.UI;

public class MQTTManager : MonoBehaviour
{
    [Header("MQTT连接状态")]
    public Image imgMqttStatus1;//首页显示
    public InputField inputBrokerIp;
    public InputField inputBrokerPort;
    public Text textPrompt;
    public Button btnConnect;

    //记录之前是否连接
    bool preConnected = false;
    bool isMqttConnected
    {
        get
        {
            if (MqttMgr.Instance != null)
                return MqttMgr.Instance.isMqttConnected;
            return false;
        }
    }

    async void Start()
    {
        btnConnect.onClick.AddListener(OnClickBtnConnect);

        if (!MqttMgr.IsInitialized)
        {
            var mqtt = MqttMgr.Instance;
            mqtt.OnConnected += OnConnectionSuccee;
            mqtt.OnDisconnected += OnDisconnected;
        }

        inputBrokerIp.text = PlayerPrefs.GetString("brokerIp", "127.0.0.1");
        inputBrokerPort.text = PlayerPrefs.GetString("brokerPort", "1883");

        MqttMgr.Instance.SetBrokerAddress(inputBrokerIp.text.Trim());
        MqttMgr.Instance.SetBrokerPort(inputBrokerPort.text.Trim());

        // 连接
        await MqttMgr.Instance.ConnectAsync();
    }

    float sumDeltaTime = 0.0f;
    // Update is called once per frame
    void Update()
    {
        if (Time.frameCount % 5 == 0)
        {
            if (preConnected != isMqttConnected)
            {
                if (isMqttConnected)
                {
                    OnConnectionSuccee();
                }
                else
                {
                    OnDisconnected("");
                }
                preConnected = isMqttConnected;
            }
        }

        if(MqttMgr.Instance.isMqttConnected)
        {
            sumDeltaTime += Time.deltaTime;
            if (sumDeltaTime > 5.0f)
            {
                sumDeltaTime = 0.0f;
                //发送心跳消息
                //CmdC0 cmdC0 = new CmdC0();
                //cmdC0.ip = _localIP;
                //cmdC0.sbh = _bindLanYiDeviceNo;
                //MqttMgr.Instance.PublishAsync(JsonUtility.ToJson(cmdC0));
            }
        }        
    }
    async void OnClickBtnConnect()
    {
        if (string.IsNullOrWhiteSpace(inputBrokerIp.text))
        {
            Message("请输入IP地址");
            return;
        }
        if (string.IsNullOrWhiteSpace(inputBrokerPort.text))
        {
            Message("请输入端口号");
            return;
        }

        PlayerPrefs.SetString("brokerIp", inputBrokerIp.text.Trim());
        PlayerPrefs.SetString("brokerPort", inputBrokerPort.text.Trim());

        if (MqttMgr.IsInitialized)
        {
            await MqttMgr.Instance.DisconnectAsync();
        }

        MqttMgr.Instance.SetBrokerAddress(inputBrokerIp.text.Trim());
        MqttMgr.Instance.SetBrokerPort(inputBrokerPort.text.Trim());
        //手动重连重置自动重连次数

        await MqttMgr.Instance.ConnectAsync();
    }

    private void Message(string content)
    {
        textPrompt.text = content;
    }

    private async void OnConnectionSuccee()
    {
        await MqttMgr.Instance.SubscribeAsync("yiyue/lanyi");
        MqttMgr.Instance.OnMessageReceived += OnProcessMessage;


        if (PubMethod.UseShakeSignal == 1)
        {
            imgMqttStatus1.color = Color.green;
        }
    }
    private void OnDisconnected(string reson)
    {
        if (PubMethod.UseShakeSignal == 1)
        {
            imgMqttStatus1.color = Color.gray;
        }
    }

    private void OnProcessMessage(string topic, string msg)
    {
        if (topic == MqttMgr.Instance.MqttTopic)
        {
            //解析MQTT消息
        }
    }


    private async Task OnDisable()
    {
        if(MqttMgr.Instance != null)
            await MqttMgr.Instance.DisconnectAsync();
    }
}
相关推荐
mxwin4 小时前
在unity shader中,通过pass产生阴影,通过主pass的光照 接收阴影!那么问题来了,是先产生阴影吗?还是先接收阴影,执行顺序是啥呢
数码相机·unity·游戏引擎·shader
小贺儿开发4 小时前
《唐朝诡事录之长安》——盛世马球
人工智能·unity·ai·shader·绘画·影视·互动
蒙双眼看世界13 小时前
Unity结合ECharts图表及网页插件EmbeddedBrowser的应用开发
unity·游戏引擎·echarts
小书房17 小时前
Java的虚拟线程
协程·异步·虚拟线程·coroutinescope
郝学胜-神的一滴18 小时前
中级OpenGL教程 004:为几何体注入法线灵魂
c++·unity·游戏引擎·godot·图形渲染·opengl·unreal
LF男男3 天前
GameManager.cs
unity
郝学胜-神的一滴4 天前
[简化版 GAMES 101] 计算机图形学 07:图形学投影完全推导
c++·unity·图形渲染·three.js·unreal engine
Avalon7125 天前
Unity3D响应式渲染UI框架UniVue
游戏·ui·unity·c#·游戏引擎
ellis19705 天前
Unity UI性能优化一之插件【Unity UI Optimization Tool】
unity·性能优化