Unity MQTT
最近、IoT関連のプロジェクトを受注し、通信にMQTTを使用する必要があったのですが、今回初めて触れたので、簡単に紹介するために文書を書きます。
序章
MQTT (Message Queuing Telemetry Transport) は、IoT デバイスとアプリケーションの接続に使用できる軽量のメッセージング プロトコルです。MQTT は 1999 年に IBM によって初めて開発され、現在はオープンな OASIS 標準です。MQTT の設計目標は、さまざまなネットワーク環境やデバイスに適した、シンプルで信頼性が高く、低消費電力で実装が簡単なメッセージ送信プロトコルを提供することです。
動作原理
MQTT プロトコルはパブリッシュ/サブスクライブ モデルを使用します。このモデルでは、メッセージ パブリッシャーがメッセージを特定のトピック (トピック) にパブリッシュし、サブスクライバーは対応するトピックをサブスクライブすることによってメッセージを受信します。MQTT サーバー (ブローカー) は、すべてのメッセージを受信し、対応するサブスクライバーにルーティングする責任を負います。MQTT は複数のサブスクライバとパブリッシャーをサポートしているため、複雑なメッセージ送信ネットワークを構築できます。Unity イベント システムを作成したことがある場合は、それが似ていることがわかるでしょう。
主流の MQTT はデータ プッシュ用の TCP 接続に基づいていますが、MQTT-SN と呼ばれる UDP ベースのバージョンもあります。この 2 つのバージョンは接続方法が異なるため、当然、メリットとデメリットも異なります。
プロトコル
-
クライアント: MQTT クライアントは、センサー、組み込みシステム、スマートフォン、デスクトップ アプリケーションなど、任意のデバイスまたはアプリケーションになります。クライアントは、MQTT サーバー (ブローカー) に接続してメッセージを送受信します。
-
トピック: MQTT メッセージは 1 つ以上のトピックにパブリッシュできます。トピックはメッセージの識別子であり、サブスクライバーはトピックをサブスクライブすることで、対応するメッセージを受信できます。
-
QoS (サービス品質): MQTT は 3 つの異なる QoS レベルをサポートします。
- QoS 0: 最大 1 回の転送。メッセージ発行者はメッセージを 1 回だけ送信し、メッセージが受信されるかどうかは保証しません。
- QoS 1: 少なくとも 1 回の転送。メッセージ発行者はメッセージを送信し、少なくとも 1 回は受信されることが保証されます。
- QoS 2: 転送は 1 回だけ。メッセージ発行者はメッセージを送信し、受信されるのは 1 回だけであることが保証されます。
-
保持メッセージ: 保持メッセージは、MQTT サーバーに保存され、新しいサブスクライバがトピックに接続したときに自動的に送信される特別なタイプのメッセージです。これは、リアルタイムのデータ更新を提供するのに役立ちます。
-
保持メッセージ: 保持メッセージは、MQTT サーバーに保存され、新しいサブスクライバがトピックに接続したときに自動的に送信される特別なタイプのメッセージです。これは、リアルタイムのデータ更新を提供するのに役立ちます。
特性
-
軽量: MQTT プロトコルは非常に軽量で、リソースに制約のあるデバイス上でも実行できます。
-
信頼性: MQTT は QoS レベルをサポートし、メッセージの信頼性の高い送信を保証します。
-
柔軟性: MQTT は複数のトピック フィルタリング方法をサポートしており、必要に応じてサブスクリプション ルールをカスタマイズできます。
-
実装の容易さ: MQTT プロトコルの実装は非常にシンプルで、さまざまなプログラミング言語やオペレーティング システムで使用できます。
-
オープン スタンダード: MQTT はオープン スタンダードであり、多くのオープン ソース実装とツールが利用可能です。
ユニティで使用する
MQTTのバージョン
現在、MQTT には MQTT3.1.1 と MQTT5 という 2 つの主流バージョンがあります。MQTT3.1.1は2014年10月にリリースされ、MQTT5は2019年3月にリリースされました。MQTT3.1.1 と MQTT5 の間には 5 年近い時間差がありますが、クラシック バージョンとしての MQTT3.1.1 は依然として主流のバージョンであり、実際のニーズのほとんどを満たすことができます。
MQTT5 は MQTT3.1.1 に基づいてアップグレードされているため、MQTT5 は MQTT3.1.1 と完全な互換性があります。MQTT5 は、MQTT3.1.1 をベースに、より多くの機能を追加し、MQTT プロトコルを補完および改善します。
ここではMQTT5を使用します。
MQTTX
MQTT X は、オープン ソースの MQTT 5.0 デスクトップ テスト クライアントで、macOS、Linux、および Windows オペレーティング システムでの実行をサポートします。
ツールのダウンロード アドレス: MQTT X: クロスプラットフォーム MQTT 5.0 デスクトップ クライアント
MQTTnet.dll
Unity で使用するには、いくつかの既製のプラグインが利用可能ですが、ここでは MQTTnet.dll を選択します。
github プロジェクト: GitHub - dotnet/MQTTnet
vs で NuGet を介して MQTTnet.dll を直接取得することもできます。
dll ファイルを取得したら、unity プロジェクトの Plugins フォルダーにインポートします。
クライアント
クライアントが実装する必要がある主な機能は、サーバーへの接続、切断後の再接続、メッセージの購読、メッセージの監視などです。
これは私が書いたデモの例です。皆さんのお役に立てれば幸いです。
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,
}
}
エピローグ
参考資料:この記事1つでMQTTが理解できる - Zhihu (zhihu.com)
文章に何か問題がある場合は、批判や修正のメッセージを残していただければ幸いです。