您现在的位置是:首页 >技术交流 >Unity MQTT网站首页技术交流

Unity MQTT

淡定九号 2024-07-18 12:01:02
简介Unity MQTT

Unity MQTT

最近接到一个物联网相关的项目,那边要求使用MQTT来进行通讯,第一次接触这个东西,所以写篇文档简单介绍下。

简介

MQTT(消息队列遥测传输) 是一种轻量级的消息传输协议,它可以用于连接 IoT 设备和应用程序。MQTT 由 IBM 在 1999 年首次开发,现在已成为一个开放的 OASIS 标准。MQTT 的设计目标是提供一个简单、可靠、低功耗、易于实现的消息传输协议,适用于各种网络环境和设备。

工作原理

MQTT 协议使用发布-订阅模式,其中消息发布者将消息发布到特定主题(Topic),而订阅者则通过订阅相应的主题来接收消息。MQTT 服务器(Broker)负责接收所有的消息,并将它们路由到对应的订阅者。MQTT 支持多个订阅者和发布者,因此可以构建复杂的消息传输网络。如果写过unity事件系统,就会发现是比较类似的。

主流的 MQTT 是基于 TCP 连接进行数据推送的,但是同样也有基于 UDP 的版本,叫做 MQTT-SN。这两种版本由于基于不同的连接方式,优缺点自然也就各有不同了。

协议

  1. 客户端(Client):MQTT 客户端可以是任何设备或应用程序,包括传感器、嵌入式系统、智能手机或桌面应用程序。客户端通过连接到 MQTT 服务器(Broker)来发送或接收消息。

  2. 主题(Topic):MQTT 消息可以被发布到一个或多个主题上。主题是消息的标识符,订阅者可以通过订阅主题来接收对应的消息。

  3. QoS(Quality of Service):MQTT 支持三种不同的 QoS 等级:

    • QoS 0:最多一次传输。消息发布者只发送一次消息,不保证消息是否被接收。
    • QoS 1:至少一次传输。消息发布者发送一条消息,确保至少被接收一次。
    • QoS 2:恰好一次传输。消息发布者发送一条消息,并确保只被接收一次。
  4. 保留消息(Retained Message):保留消息是一种特殊类型的消息,它会被保存在 MQTT 服务器上,并在新的订阅者连接到主题时自动发送。这对于提供实时数据更新非常有用。

  5. 保留消息(Retained Message):保留消息是一种特殊类型的消息,它会被保存在 MQTT 服务器上,并在新的订阅者连接到主题时自动发送。这对于提供实时数据更新非常有用。

特性

  1. 轻量级:MQTT 协议非常轻量,可以在资源受限的设备上运行。

  2. 可靠性:MQTT 支持 QoS 等级,可以确保消息的可靠传输。

  3. 灵活性:MQTT 支持多种主题过滤方式,可以根据需要定制订阅规则。

  4. 易于实现:MQTT 协议的实现非常简单,可以在各种编程语言和操作系统上使用。

  5. 开放标准:MQTT 是一个开放标准,有许多开源实现和工具可供使用。

unity中的使用

MQTT版本

目前 MQTT 主流版本有两个,分别是 MQTT3.1.1 和 MQTT5。MQTT3.1.1 是在 2014 年 10 月发布的,而 MQTT5 是在 2019 年 3 月发布的。虽然 MQTT3.1.1 与 MQTT5 在时间相差了将近五年,但是 MQTT3.1.1作为一个经典的版本,目前仍然是主流版本,能够满足大部分实际需求。

MQTT5 是在 MQTT3.1.1 的基础上进行了升级,因此 MQTT5 是完全兼容 MQTT3.1.1 的。而 MQTT5 是 在 MQTT3.1.1 的基础上添加了更多的功能、补充完善 MQTT 协议。

在这里我们使用MQTT5。

MQTT X

MQTT X 是一款开源的 MQTT 5.0 桌面测试客户端,它支持在 macOS,Linux,Windows 操作系统上运行。

工具下载地址:MQTT X: Cross-platform MQTT 5.0 Desktop Client

MQTTnet.dll

要在unity中使用,有一些现成的插件可以使用,这里我们选择了MQTTnet.dll。

github工程:GitHub - dotnet/MQTTnet

也可以在vs中通过NuGet来直接MQTTnet.dll。

获取到dll文件后,导入到unity工程中的Plugins文件夹下就可以。

客户端

客户端主要需要实现的功能就是连接服务器,断线重连,消息订阅,消息监听等。

这是我自己写的一个例子DEMO,希望能帮助到大家。

using System.Collections;
using System.Collections.Generic;
using UnityEngine;
using MQTTnet.Client;
using MQTTnet;
using System.Threading.Tasks;
using MQTTnet.Packets;
using Newtonsoft.Json.Linq;
using Newtonsoft.Json;

/// <summary>
/// MQTT管理器
/// </summary>
public class MQTTManager : MonoSingleton<MQTTManager>
{
    private static readonly string MQTTURI = "broker.emqx.io";
    private static readonly int MQTTPort = 1883;
    private static readonly string MQTTUser = "";
    private static readonly string MQTTPassword = "";

    private string _ClientID;

    /// <summary>
    /// 重连间隔时间(s)
    /// </summary>
    private static readonly float ReconnectGapTime = 10;

    /// <summary>
    /// 客户端
    /// </summary>
    private IMqttClient _Client;

    /// <summary>
    /// 失败次数
    /// </summary>
    private int _FailCount;

    /// <summary>
    /// 当前状态
    /// </summary>
    private MQTTStatus _Status;

    /// <summary>
    /// 等待时间
    /// </summary>
    private float _WaitTime;

    // Start is called before the first frame update
    void Start()
    {
        _ClientID = System.Guid.NewGuid().ToString();
        Task.Run(InitMQTT);
    }

    // Update is called once per frame
    void Update()
    {
        if (_Status == MQTTStatus.Failed)
        {
            _WaitTime += Time.deltaTime;
            if (_WaitTime > ReconnectGapTime)
            {
                _WaitTime = 0;
                Reconnect();
            }
        }
    }

    /// <summary>
    /// 初始化MQTT信息
    /// </summary>
    private void InitMQTT()
    {
        MqttClientOptionsBuilder builder = new MqttClientOptionsBuilder()
                .WithTcpServer(MQTTURI, MQTTPort) // 要访问的mqtt服务端的 ip 和 端口号
                .WithCredentials(MQTTUser, MQTTPassword) // 要访问的mqtt服务端的用户名和密码
                .WithClientId(_ClientID) // 设置客户端id
                .WithCleanSession()
                .WithTls(new MqttClientOptionsBuilderTlsParameters
                {
                    UseTls = false  // 是否使用 tls加密
                });
        MqttClientOptions clientOptions = builder.Build();
        _Client = new MqttFactory().CreateMqttClient();

        _Client.ConnectedAsync += Client_ConnectedAsync; // 客户端连接成功事件
        _Client.DisconnectedAsync += Client_DisconnectedAsync; // 客户端连接关闭事件
        _Client.ApplicationMessageReceivedAsync += Client_ApplicationMessageReceivedAsync; ; // 收到消息事件

        _Status = MQTTStatus.Connecting;
        _Client.ConnectAsync(clientOptions);
    }

    /// <summary>
    /// 重新连接
    /// </summary>
    private void Reconnect()
    {
        LogManager.LogInfo("重新连接");
        Task.Run(delegate ()
        {
            _Status = MQTTStatus.Connecting;
            _Client.ReconnectAsync();
        });
    }

    /// <summary>
    /// 新消息事件
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    private System.Threading.Tasks.Task Client_ApplicationMessageReceivedAsync(MqttApplicationMessageReceivedEventArgs arg)
    {
        string str = System.Text.Encoding.UTF8.GetString(arg.ApplicationMessage.PayloadSegment.Array);
        Loom.QueueOnMainThread(() =>
        {
            LogManager.LogInfo("收到消息:" + arg.ApplicationMessage.Topic + "=====" + str);
        });
        return Task.CompletedTask;
    }

    /// <summary>
    /// 连接断开事件
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    private System.Threading.Tasks.Task Client_DisconnectedAsync(MqttClientDisconnectedEventArgs arg)
    {
        Loom.QueueOnMainThread(() =>
        {
            LogManager.LogInfo("MQTT连接断开:" + arg.Reason);
        });
        _Status = MQTTStatus.Failed;
        _FailCount++;
        return Task.CompletedTask;
    }

    /// <summary>
    /// 连接成功事件
    /// </summary>
    /// <param name="arg"></param>
    /// <returns></returns>
    private System.Threading.Tasks.Task Client_ConnectedAsync(MqttClientConnectedEventArgs arg)
    {
        _Status = MQTTStatus.Connected;
        Loom.QueueOnMainThread(() =>
        {
            LogManager.LogInfo("MQTT连接成功");
            _FailCount = 0;
        });

        return Task.CompletedTask;
    }

    /// <summary>
    /// 发布消息
    /// </summary>
    public static void PublishAsync(MqttApplicationMessage message)
    {
        if (_Instance)
        {
            _Instance._Client.PublishAsync(message);
        }
    }

    /// <summary>
    /// 订阅消息
    /// </summary>
    public static void SubscribeAsync(MqttClientSubscribeOptions options)
    {
        if (_Instance)
        {
            _Instance._Client.SubscribeAsync(options);
        }
    }

    /// <summary>
    /// 取消订阅消息
    /// </summary>
    public static void UnsubscribeAsync(MqttClientUnsubscribeOptions options)
    {
        if (_Instance)
        {
            _Instance._Client.UnsubscribeAsync(options);
        }
    }

    /// <summary>
    /// 状态
    /// </summary>
    public enum MQTTStatus
    {
        Empty = 0,

        /// <summary>
        /// 连接中
        /// </summary>
        Connecting = 1,

        /// <summary>
        /// 连接成功
        /// </summary>
        Connected = 2,

        /// <summary>
        /// 连接失败
        /// </summary>
        Failed = 3,
    }
}

尾语

参考文档:一文带你搞懂 MQTT - 知乎 (zhihu.com)

如果有写的不好的地方,欢迎各位大佬留言批评指正。

风语者!平时喜欢研究各种技术,目前在从事后端开发工作,热爱生活、热爱工作。