IT数码 购物 网址 头条 软件 日历 阅读 图书馆
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
图片批量下载器
↓批量下载图片,美女图库↓
图片自动播放器
↓图片自动播放器↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁
 
   -> 游戏开发 -> ★UNITY实战进阶-C# MQTT (消息队列遥测传输协议)详解-8 -> 正文阅读

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

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

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

  • MQTT介绍

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

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

  • ?MQTTnet

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

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

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

?你们要啥子版本就从里面拿,这里我们使用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<MqttClientObject> m_MqttClientObject = new List<MqttClientObject>();

        public Action<string, string> 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<int> 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!");
        }

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

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

        /// <summary>
        /// 客户端断开消息
        /// </summary>
        /// <param name="eventArgs"></param>
        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, "客户端已断开"); }); });
        }

        /// <summary>
        /// 接收到的应用程序消息处理
        /// </summary>
        /// <param name="eventArgs"></param>
        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);
                });
            });
        }

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

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

        /// <summary>
        /// 推送所有的主题是clinetId的客户端
        /// </summary>
        /// <param name="message"></param>
        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);
            }
        }

        /// <summary>
        /// 推送监听某主题的客户端
        /// </summary>
        /// <param name="topic"></param>
        /// <param name="message"></param>
        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<string, string> MessageCallback = null;
        public Action<MqttClient> ConnectCallback = null;
        public Action<MqttClient> DisconnectCallback = null;

        /// <summary>
        /// 推送下来的消息
        /// </summary>
        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); }); });
        }

        /// <summary>
        /// 客户端推送信息 
        /// </summary>
        /// <param name="clientID"></param>
        /// <param name="message"></param>
        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(* ̄▽ ̄*)ブ

  游戏开发 最新文章
6、英飞凌-AURIX-TC3XX: PWM实验之使用 GT
泛型自动装箱
CubeMax添加Rtthread操作系统 组件STM32F10
python多线程编程:如何优雅地关闭线程
数据类型隐式转换导致的阻塞
WebAPi实现多文件上传,并附带参数
from origin ‘null‘ has been blocked by
UE4 蓝图调用C++函数(附带项目工程)
Unity学习笔记(一)结构体的简单理解与应用
【Memory As a Programming Concept in C a
上一篇文章      下一篇文章      查看所有文章
加:2021-11-22 12:40:53  更:2021-11-22 12:41:43 
 
开发: C++知识库 Java知识库 JavaScript Python PHP知识库 人工智能 区块链 大数据 移动开发 嵌入式 开发工具 数据结构与算法 开发测试 游戏开发 网络协议 系统运维
教程: HTML教程 CSS教程 JavaScript教程 Go语言教程 JQuery教程 VUE教程 VUE3教程 Bootstrap教程 SQL数据库教程 C语言教程 C++教程 Java教程 Python教程 Python3教程 C#教程
数码: 电脑 笔记本 显卡 显示器 固态硬盘 硬盘 耳机 手机 iphone vivo oppo 小米 华为 单反 装机 图拉丁

360图书馆 购物 三丰科技 阅读网 日历 万年历 2024年11日历 -2024/11/27 22:38:40-

图片自动播放器
↓图片自动播放器↓
TxT小说阅读器
↓语音阅读,小说下载,古典文学↓
一键清除垃圾
↓轻轻一点,清除系统垃圾↓
图片批量下载器
↓批量下载图片,美女图库↓
  网站联系: qq:121756557 email:121756557@qq.com  IT数码