深入浅出MQTT: Unity客户端和服务端

(本文把服务器搭建和unity客户端代码一起讲了,文章可能会有点长,请耐心看完)

MQTT是IBM开源的一个通讯方式,如果你是在做物联网或传感器之类的通讯,这个通讯方式十分适合你,下面讲一下MQTT的优点:

  • (1)精简,不添加可有可无的功能;
  • (2)发布/订阅(Pub/Sub)模式,方便消息在传感器之间传递;
  • (3)允许用户动态创建主题,零运维成本;
  • (4)把传输量降到最低以提高传输效率;
  • (5)把低带宽、高延迟、不稳定的网络等因素考虑在内;
  • (6)支持连续的会话控制;
  • (7)理解客户端计算能力可能很低;
  • (8)提供服务质量管理;
  • (9)假设数据不可知,不强求传输数据的类型与格式,保持灵活性。

再看看百度百科它的应用:

深入浅出MQTT: Unity客户端和服务端_第1张图片

是不是很牛逼呢?

作为一种低开销、低带宽占用的即时通讯协议,使其在物联网、小型设备、移动应用等方面有较广泛的应用。

再具体看看它的通讯方式结构:

 

深入浅出MQTT: Unity客户端和服务端_第2张图片

 

解释一下:实现MQTT协议需要客户端和服务器端通讯完成,在通讯过程中,MQTT协议中有三种身份:发布者(Publish)、代理(Broker)(服务器)、订阅者(Subscribe)。其中,消息的发布者和订阅者都是客户端,消息代理是服务器,消息发布者可以同时是订阅者.

那么,我们就明白了,如果我们需要在本地搭建一套MQTT的通讯方式,至少有两个身份,Broker(也就是服务器)和客户端,因为客户端可以同时是订阅者和发布者,服务器只是作为中转消息的作用,其实消息的真实来源是发布者。

这次我们使用unity来实现这一个过程,首先我们先建立以下服务器,服务器我们采用apache-apollo,文章最后会提供包

接着,我们来讲一下怎么搭建apache-apollo,下载后文末提供的压缩包,解压之后,在bin目录下,按住键盘shift+鼠标右键,打开命令行:输入命令apollo.cmd create mybroker

深入浅出MQTT: Unity客户端和服务端_第3张图片

如果没有这样的画面出现或者报错,请先检测自己电脑的java环境变量是否正常,可以打开cmd命令行,输入javac查看一下,如果有相关信息,就是正确的环境

上面的命令无误执行完之后,会生成一个新的文件夹:

 

深入浅出MQTT: Unity客户端和服务端_第4张图片

进入该文件夹的bin目录,再次按住shift+鼠标右键,敲击下面的命令: "apollo-broker" run,最好不要中文路径

深入浅出MQTT: Unity客户端和服务端_第5张图片

服务器就这样启动了,我们可以在浏览器上面输入http://127.0.0.1:61680进入服务器页面,默认账号密码是 admin  password

深入浅出MQTT: Unity客户端和服务端_第6张图片

 

为了更好的在本机测试,我们修改一下配置文件,重新启动一下服务器,在生成的文件夹下面的etc文件夹,找到这个文件,打开:

深入浅出MQTT: Unity客户端和服务端_第7张图片

找到下图的内容,把0.0.0.0修改为127.0.0.1,保存后重启服务器:

深入浅出MQTT: Unity客户端和服务端_第8张图片

深入浅出MQTT: Unity客户端和服务端_第9张图片

深入浅出MQTT: Unity客户端和服务端_第10张图片

待会我们直接连上这个地址就行了。

接下来,轮到我们熟悉的unity了,就是我们的主场了啊,首先拷贝好相关的库文件,后面我会一起提供了,不要着急,先来看看代码:

using System;
using System.Collections;
using System.Collections.Generic;
using System.Net;
using uPLibrary.Networking.M2Mqtt;
using uPLibrary.Networking.M2Mqtt.Messages;
using UnityEngine;
using UnityEngine.UI;

public class MQTT_Manager : MonoBehaviour
{
    [SerializeField] private InputField ip;
    [SerializeField] private InputField port;
    [SerializeField] private InputField subscribe_chanel;
    [SerializeField] private InputField publish_chanel;
    [SerializeField] private InputField publish_content;
    [SerializeField] private Button connect_btn;
    [SerializeField] private Button subscribe_btn;

    [SerializeField] private Button publish_btn;
    [SerializeField] private Text receive_Message_text;
    private MqttClient client;

    private string receive_message;
    // Use this for initialization
    void Start () {
        //连接按钮监听事件
		connect_btn.onClick.AddListener(() =>
		{
		    string txtIP = ip.text;
		    string txtPort = port.text;
		    string clientId = Guid.NewGuid().ToString();
            //服务器默认密码是这个
		    string username = "admin";
		    string password = "password";
		    client = new MqttClient(IPAddress.Parse(txtIP), int.Parse(txtPort), false, null);

            client.MqttMsgPublishReceived += Client_MqttMsgPublishReceived;
            client.MqttMsgSubscribed += Client_MqttMsgSubscribed; ;
		    client.Connect(clientId, username, password);

		});
        //订阅按钮监听事件
        subscribe_btn.onClick.AddListener(() =>
        {
            if (client != null&&subscribe_chanel.text!="")
            {
                client.Subscribe(new string[] { subscribe_chanel.text }, new byte[] { MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE });
            }
        });

        //发布按钮监听事件
        publish_btn.onClick.AddListener(() =>
        {
            if (client != null && publish_chanel.text != "")
            {
                client.Publish(publish_chanel.text, System.Text.Encoding.UTF8.GetBytes(publish_content.text), MqttMsgBase.QOS_LEVEL_EXACTLY_ONCE, false);
            }
        });

    }

    private void Client_MqttMsgSubscribed(object sender, uPLibrary.Networking.M2Mqtt.Messages.MqttMsgSubscribedEventArgs e)
    {
        Debug.Log("订阅" + e.MessageId);
    }

    private void Client_MqttMsgPublishReceived(object sender, uPLibrary.Networking.M2Mqtt.Messages.MqttMsgPublishEventArgs e)
    {
        string message=System.Text.Encoding.UTF8.GetString(e.Message);
        receive_message = message;
        Debug.Log("接收到消息是"+message);
    }

    // Update is called once per frame
    void Update ()
    {
        receive_Message_text.text = receive_message;
    }
}

再看看我的界面:

深入浅出MQTT: Unity客户端和服务端_第11张图片

如果你想更好的理解MQTT,建议了发布一个程序出去,一个当发布者,编辑器的程序当订阅者:

如果你想收到消息,订阅的频道必须跟发布消息的一致,下面我在同一个客户端演示一下:

深入浅出MQTT: Unity客户端和服务端_第12张图片

连接之后,再看看我们服务器后台:

深入浅出MQTT: Unity客户端和服务端_第13张图片

连上了是吧

深入浅出MQTT: Unity客户端和服务端_第14张图片

明白了吧,最后讲几个MQTT比较重要的点:

MQTT有三种消息发布服务质量:

    • “至多一次”,消息发布完全依赖底层 TCP/IP 网络。会发生消息丢失或重复。这一级别可用于如下情况,环境传感器数据,丢失一次读记录无所谓,因为不久后还会有第二次发送。
    • “至少一次”,确保消息到达,但消息重复可能会发生。
    • “只有一次”,确保消息到达一次。这一级别可用于如下情况,在计费系统中,消息重复或丢失会导致不正确的结果

具体使用哪一种,看你的使用场景,看看我们代码中是怎么调这个的,

这个是只有一次,英文的事情自己多读读哈,看看源码:

深入浅出MQTT: Unity客户端和服务端_第15张图片

还有一个,鼠标放上去,看看这个方法:

retain:true:表示发送的消息需要一直持久保存(不受服务器重启影响),不但要发送给当前的订阅者,并且以后新来的订阅了此Topic name的订阅者会马上得到推送。
备注:新来乍到的订阅者,只会取出最新的一个RETAIN flag = 1的消息推送。
false:仅仅为当前订阅者推送此消息。
假如服务器收到一个空消息体(zero-length payload)、RETAIN = 1、已存在Topic name的PUBLISH消息,服务器可以删除掉对应的已被持久化的PUBLISH消息。

希望对大家有用,如果你觉得我写得不错的话,你可以关注我,如果你对UE4,python和js有兴趣,也可以关注我:

本文的Unity源码的地址是https://github.com/Leemu0822/MQTT_Unity

不用github的兄弟或者你可以关注我的公众号,回复关键字"MQTT"获取源码和服务器包



 

 

 

 

 

你可能感兴趣的:(Unity3D)