★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8

        最近在做物联网相关的内容,本打算用socket去做,但对于硬件来说,需要一个体量小、精简、低带宽和不稳定的网络环境中提供可靠的网络服务、方便快捷,于是我在这方面优先选择了MQTT协议。

        由于是做本地内网传输, Unity项目作为服务器,这还不是想怎么玩就怎么玩么。
        简而言之、就是玩儿~

  • MQTT介绍

        这块百度一搜都有了,我简单点说,划几个重点。

        1.对于没做过服务器开发的人员来说,开发是真的简单。
        2.防火墙穿透。
        3.应用广泛,几乎现在随便找一家大型的硬件、互联网企业,都可以找到MQTT的身影,例如Facebook、alibaba、baidu等等。
        4.无法做负载均衡。
        5.同一局域网,有设备A和设备B分别建立本地服务器,在同一主题的情况下,客户端C连接设备A并监听,那么设备B能监听到是谁发送的,需要注意消息的安全性。

★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8_第1张图片

  •  MQTTnet

        在unity上使用C# 版本的 下载链接:https://github.com/chkr1011/MQTTnethttps://github.com/chkr1011/MQTTnet

★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8_第2张图片

 ​运行,找到MQTTnet项目,生成它!

★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8_第3张图片

我们就能在bin中找到这个dll 

★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8_第4张图片

 你们要啥子版本就从里面拿,这里我们使用net452/MQTTnet.dll

然后,老规矩塞到unity的plugins里面。


  • MQTT Server

using MQTTnet;
using MQTTnet.Diagnostics.Logger;
using MQTTnet.Protocol;
using MQTTnet.Server;
using System;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using System.Threading.Tasks;
using UnityEngine;

namespace MQTT
{
    public class MqttServer
    {
        private IMqttServer m_MqttServer = null;

        private List m_MqttClientObject = new List();

        public Action MessageCallback = null;

        public bool IsConnect
        {
            get
            {
                if (m_MqttServer == null)
                    return false;
                return m_MqttServer.IsStarted;
            }
        }

        public MqttServer()
        {
            m_MqttClientObject.Clear();
        }

        public void OpenMqttServer(int port = 3883)
        {
            var server_logger = new MqttNetEventLogger("server log");
            server_logger.LogMessagePublished += (sender, e) =>
            {
                //Debug.Log(e.LogMessage.ToString());
            };

            m_MqttServer = new MqttFactory().CreateMqttServer(server_logger);
            var optionbuilder = new MqttServerOptionsBuilder();
            //默认端口是1883,这里可以自己设置
            optionbuilder.WithDefaultEndpointPort(port);
            optionbuilder.WithConnectionValidator(WithConnectionValidator);

            m_MqttServer.StartedHandler = new MqttServerStartedHandlerDelegate(StartedHandler);
            m_MqttServer.StoppedHandler = new MqttServerStoppedHandlerDelegate(StoppedHandler);
            m_MqttServer.UseClientConnectedHandler(UseClientConnectedHandler);
            m_MqttServer.UseClientDisconnectedHandler(UseClientDisconnectedHandler);
            m_MqttServer.UseApplicationMessageReceivedHandler(UseApplicationMessageReceivedHandler);
            m_MqttServer.ClientSubscribedTopicHandler = new MqttServerClientSubscribedTopicHandlerDelegate(ClientSubscribedTopicHandler);
            m_MqttServer.ClientUnsubscribedTopicHandler = new MqttServerClientUnsubscribedTopicHandlerDelegate(ClientUnsubscribedTopicHandler);

            m_MqttServer.StartAsync(optionbuilder.Build());
        }

        public void Disconnect()
        {
            m_MqttServer.StopAsync();
        }

        public async Task ConnectClientCount()
        {
            if (m_MqttServer == null) return 0;
            var msgs = await m_MqttServer.GetClientStatusAsync();
            return msgs.Count;
        }

        private void WithConnectionValidator(MqttConnectionValidatorContext context)
        {
            //这里可以校验连接client的username、password   也可以选择不校验
            if (string.IsNullOrWhiteSpace(context.Username) || string.IsNullOrWhiteSpace(context.Password))
            {
                Debug.Log($"用户:{context.ClientId}登录失败,用户信息为空");

                context.ReasonCode = MqttConnectReasonCode.BadUserNameOrPassword;
                context.ReasonString = "登录失败,用户名或密码为空";
                return;
            }
            if (context.Username.Equals("admin") && context.Password.Equals("123456"))
            {
                m_MqttClientObject.Add(new MqttClientObject() { ClientID = context.ClientId, UserName = context.Username, PassWord = context.Password });
                context.ReasonCode = MqttConnectReasonCode.Success;
                context.ReasonString = "登录成功";
            }
        }

        private void StartedHandler(EventArgs eventArgs)
        {
            Debug.Log("服务: started!");

            Loom.RunAsync(() =>
            {
                Loom.QueueOnMainThread(() =>
                {
                    MessageCallback?.Invoke("Start Server", "服务: started!");
                });
            });
        }

        private void StoppedHandler(EventArgs eventArgs)
        {
            Debug.Log("服务: stopped!");
        }

        /// 
        /// 客户端连接消息
        /// 
        /// 
        private void UseClientConnectedHandler(MqttServerClientConnectedEventArgs eventArgs)
        {
            Debug.Log($"服务:client Connected ClientId:{eventArgs.ClientId}");

            Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(eventArgs.ClientId, "客户端已连接"); }); });
        }

        /// 
        /// 客户端断开消息
        /// 
        /// 
        private void UseClientDisconnectedHandler(MqttServerClientDisconnectedEventArgs eventArgs)
        {
            Debug.Log($"服务:client Disconnected ClientId:{eventArgs.ClientId} Type:{eventArgs.DisconnectType}");
            for (int i = 0; i < m_MqttClientObject.Count; i++)
            {
                if (m_MqttClientObject[i].ClientID.Equals(eventArgs.ClientId))
                {
                    m_MqttClientObject.RemoveAt(i);
                    break;
                }
            }

            Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(eventArgs.ClientId, "客户端已断开"); }); });
        }

        /// 
        /// 接收到的应用程序消息处理
        /// 
        /// 
        private void UseApplicationMessageReceivedHandler(MqttApplicationMessageReceivedEventArgs eventArgs)
        {
            if (string.IsNullOrEmpty(eventArgs.ClientId)) return;

            var msg = eventArgs.ApplicationMessage;
            var topic = msg.Topic;
            var payload = msg.ConvertPayloadToString();
            Debug.Log($"服务:ClientId:'{eventArgs.ClientId}' Topic:'{topic}'   Payload:'{payload}'");

            Loom.RunAsync(() =>
            {
                Loom.QueueOnMainThread(() =>
                {
                    MessageCallback?.Invoke(eventArgs.ClientId, payload);
                });
            });
        }

        /// 
        /// 客户端订阅的主题
        /// 
        /// 
        private void ClientSubscribedTopicHandler(MqttServerClientSubscribedTopicEventArgs eventArgs)
        {
            Debug.Log($"服务: topic subscribed ClientId:{eventArgs.ClientId} Topic:{eventArgs.TopicFilter}");
        }

        /// 
        /// 客户端取消订阅的主题
        /// 
        /// 
        private void ClientUnsubscribedTopicHandler(MqttServerClientUnsubscribedTopicEventArgs eventArgs)
        {
            Debug.Log($"服务: topic ubsubscribed ClientId:{eventArgs.ClientId} Topic:{eventArgs.TopicFilter}");
        }

        /// 
        /// 推送所有的主题是clinetId的客户端
        /// 
        /// 
        public void SendMessage(string message)
        {
            for (int i = 0; i < m_MqttClientObject.Count; i++)
            {
                m_MqttServer.PublishAsync(new MqttApplicationMessage
                {
                    Topic = m_MqttClientObject[i].ClientID,
                    QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                    Retain = false,
                    Payload = Encoding.UTF8.GetBytes(message),
                }, CancellationToken.None);
            }
        }

        /// 
        /// 推送监听某主题的客户端
        /// 
        /// 
        /// 
        public void SendMessage(string topic, string message)
        {
            m_MqttServer.PublishAsync(new MqttApplicationMessage
            {
                Topic = topic,
                QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                Retain = false,
                Payload = Encoding.UTF8.GetBytes(message),
            }, CancellationToken.None);
        }
    }


}

注意:
1.log我去掉了
2.WithConnectionValidator可以校验连接client的信息
3.如果回调中包含 赋值Unity主线程的代码,需要使用Loom配合的
4.PublishAsync, 服务器topic传递的是客户端监听的topic,
例如:
客户端
m_MqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic("Data").Build());
服务器端   
         m_MqttServer.PublishAsync(new MqttApplicationMessage
          {
                Topic = "Data",
                QualityOfServiceLevel = MqttQualityOfServiceLevel.AtMostOnce,
                Retain = false,
                Payload = Encoding.UTF8.GetBytes(message),
          }, CancellationToken.None);
这样服务器推送的消息,这个客户端才能收到,否则是没有客户端能接到这个推送


  • MQTT Clinet

using MQTTnet;
using MQTTnet.Client;
using MQTTnet.Client.Connecting;
using MQTTnet.Client.Disconnecting;
using MQTTnet.Client.Options;
using MQTTnet.Client.Receiving;
using MQTTnet.Protocol;
using System;
using System.Collections;
using System.Collections.Generic;
using System.Text;
using System.Threading;
using UnityEngine;

namespace MQTT
{
    public class MqttClient
    {
        private IMqttClient m_MqttClient;
        private string m_ClientID;
        public Action MessageCallback = null;
        public Action ConnectCallback = null;
        public Action DisconnectCallback = null;

        /// 
        /// 推送下来的消息
        /// 
        public string Message
        {
            get;
            private set;
        }

        public bool IsConnect
        {
            get
            {
                if (m_MqttClient == null) return false;
                return m_MqttClient.IsConnected;
            }
        }

        public string ClientId
        {
            get
            {
                return m_ClientID;
            }
        }

        public MqttClient(string clientID, string ip = "127.0.0.1", int port = 3883)
        {
            m_ClientID = clientID;
            var options = new MqttClientOptionsBuilder()
            .WithClientId(m_ClientID)
            .WithTcpServer(ip, port)
            .WithCredentials("admin", "123456")
            .Build();

            m_MqttClient = new MqttFactory().CreateMqttClient();
            m_MqttClient.ConnectedHandler = new MqttClientConnectedHandlerDelegate(MqttClient_Connected);
            m_MqttClient.DisconnectedHandler = new MqttClientDisconnectedHandlerDelegate(MqttClient_Disconnected);
            //m_MqttClient.ApplicationMessageReceivedHandler = new MqttApplicationMessageReceivedHandlerDelegate(MqttClient_Recevied);
            m_MqttClient.UseApplicationMessageReceivedHandler(MqttClient_Recevied);
            m_MqttClient.ConnectAsync(options);
        }

        public void Disconnect()
        {
            if (m_MqttClient != null)
                m_MqttClient.DisconnectAsync();
        }

        private void MqttClient_Connected(MqttClientConnectedEventArgs eventArgs)
        {
            Debug.Log($"mqtt client '{m_ClientID}' started!");
            //关联服务端订阅, 用于接受服务端推送信息
            if (m_MqttClient != null)
                m_MqttClient.SubscribeAsync(new MqttTopicFilterBuilder().WithTopic(m_ClientID).Build());

            Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { ConnectCallback?.Invoke(this); }); });
        }

        private void MqttClient_Disconnected(MqttClientDisconnectedEventArgs eventArgs)
        {
            Debug.Log($"mqtt client '{m_ClientID}' stopped!");
            Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { DisconnectCallback?.Invoke(this); }); });
        }

        private void MqttClient_Recevied(MqttApplicationMessageReceivedEventArgs eventArgs)
        {
            if (eventArgs.ApplicationMessage.Topic.Equals("Client")) return;

            Message = Encoding.UTF8.GetString(eventArgs.ApplicationMessage.Payload);
            Debug.Log($"mqtt client '{eventArgs.ClientId}'  Topic:{eventArgs.ApplicationMessage.Topic}    Recevied: '{Message}'");
            Loom.RunAsync(() => { Loom.QueueOnMainThread(() => { MessageCallback?.Invoke(m_ClientID, Message); }); });
        }

        /// 
        /// 客户端推送信息 
        /// 
        /// 
        /// 
        public void SendMessage(string message)
        {
            if (m_MqttClient == null) return;

            m_MqttClient.PublishAsync(new MqttApplicationMessage
            {
                Topic = "Client",
                QualityOfServiceLevel = MqttQualityOfServiceLevel.ExactlyOnce,
                Retain = false,
                Payload = Encoding.UTF8.GetBytes(message),
            }, CancellationToken.None);

        }
    }
}
  •  本地功能测试


 有兴趣的小伙伴可以关注一波

 o(* ̄▽ ̄*)ブ

你可能感兴趣的:(UNITY实战进阶,unity,c#,算法)