はじめに
VPoPの岩田です。本記事は、先日より開始しているMQTT5連載の第3回目になります。 今回より、そろそろ本格的な解説に移っていきます。
第3回目となる本記事では、まずMQTT5のプロトコル仕様をざっくりと掴むために、コントロールパケットの構造を解説していきます。 MQTTのコントロールパケットの基本的な構造を知っておくことで、MQTT5で追加されたフィールドがどこに追加されたのかが把握しやすくなります。
すでに公開されている連載記事は、末尾のリンクからご覧いただけます。
- はじめに
- 当社プロダクトの簡単なご紹介
- MQTTコントロールパケットの構造
- Fixed Header | 固定ヘッダー
- Variable Header | 可変ヘッダー
- Payload | ペイロード
- おわりに
- 連載記事のご紹介
当社プロダクトの簡単なご紹介
弊社アプトポッドでは、IoTシステムの通信バックエンドとしてデファクトスタンダードとなっているMQTTに代わる選択肢として、独自開発の通信ミドルウェア「intdash」 を開発・提供しています。 intdashでは、MQTTの旧バージョンであるMQTT 3.1.1が抱えていたスケーラビリティやパフォーマンスに関わる課題を、MQTT5とは異なるアプローチによって解決しています。
intdashについては、連載1日目の記事でももう少し詳しく触れていますので、よろしければこちらの記事もご覧ください。
また、intdashで使用している当社独自の通信プロトコルについても以下の記事で解説していますので、こちらも併せてご覧ください。
また、システム構築の受託やご相談も受け付けておりますので、IoTシステムの構築でなにかお困りのことがあれば、お気軽に弊社までお問い合わせください。
それでは、本編に入っていきます。
MQTTコントロールパケットの構造
★MQTT3.1.1から変更なし
MQTTのコントロールパケットは、以下の構造となっています。
- Fixed Header(全てのコントロールパケットに含まれる)
- Variable Header(一部のコントロールパケットのみに含まれる)
- Payload(一部のコントロールパケットのみに含まれる)
コントロールパケットの全体構造は、MQTT 3.1.1から変わっていません。
Fixed Header | 固定ヘッダー
★MQTT3.1.1から変更なし
Fixed Headerは全てのコントロールパケットに存在し、以下の構造をとります。
内容は極めて単純で、以下の3つの情報のみを格納します。
- MQTT Control Packet Type: どのコントロールパケットか
- Flags: 各タイプごとに異なるフラグ値を格納する4bitのフィールド
- Remaining Length: パケットの残りの部分の長さ
Fixed Headerの構造についても、MQTT 3.1.1から変わっていません。
MQTT Control Packet Type
★MQTT5で変更あり
MQTT Control Packet Typeは以下のような定義になっており、それぞれのパケットごとに数値が割り当てられています。
MQTT 3.1.1との違いは、以下の2点のみです。
- 15番がReservedからAUTHに変更(新しいメッセージの追加)
- DISCONNECTのDirection of flowで、サーバーからクライアントへの送信が追加
Flags
★MQTT3.1.1から変更なし
フラグ用の4ビットの定義は以下のようになっています。
MQTT 3.1.1から存在するメッセージについては、定義の変更はありません。
PUBLISHメッセージ以外のメッセージでは将来の仕様のためにReserved(固定値)となっており、現時点でこのフィールドを活用しているのはPUBLISHメッセージのみです。 PUBLISHメッセージのFlagsに関する定義は、OASIS仕様書の 「3.3.1 PUBLISH Fixed Header」から確認できます。
Remaining Length
★MQTT3.1.1から変更なし
Remaining Lengthは、Variable Byte Integerという可変長整数でエンコードされ、可変のフィールド長を持ちます。 Remaining Lengthに格納される長さは、Variable HeaderとPayloadを足した長さになります。
このフィールドについても、MQTT 3.1.1から定義は変わっていません。
Variable Header | 可変ヘッダー
★MQTT3.1.1から変更なし
Variable Headerは一部のコントロールパケットに存在し、その中身はコントロールパケットの種類ごとに異なります。 以降では、複数のコントロールパケットに共通して含まれる以下のフィールドについて解説します。
- Packet Identifier(パケット識別子)
- Properties(プロパティ)
- Reason Code(結果コード)
Packet Identifier
★MQTT3.1.1から変更なし
以下のコントロールパケットには、Packet Identifier(パケット識別子)と呼ばれるフィールドが存在します。
- PUBLISH (QoS > 0)
- PUBACK、PUBREC、PUBREL、PUBCOMP
- SUBSCRIBE、SUBACK
- UNSUBSCRIBE、UNSUBACK
Packet Identifierは、ACKが必要なメッセージに対して元のメッセージとACKメッセージを紐付けるために使用されます。
このフィールドについても、MQTT 3.1.1から定義は変わっていません。
Properties
★MQTT5で追加
PINGREQ、PINGRESP以外のコントロールパケットには、一連のプロパティ(Properties)が含まれるようになりました。
プロパティはその長さと内容をセットで1つとして扱われ、一連のプロパティには複数のプロパティが含まれます。 プロパティの内容には、Variable Byte Integerで表された識別子と実際のデータが含まれます。 言葉での説明では分かりにくいですが、図で表すと以下のような単純な構造であることが分かります。
以下に、仕様書に記載されているプロパティの定義を掲載します。
プロパティは、MQTT 5.0で追加された新しい考え方です。
User Properties
★MQTT5で追加
プロパティに関して特筆すべきは、38番で定義されている User Property(ユーザープロパティ)です。 ユーザープロパティはその他のプロパティのように事前に定義されているものではなく、ユーザーが自由に定義して使用できるプロパティになります。 自由な情報を定義できるため、任意の文字列によるキーバリューペアとして定義されます。 ただし、「ユーザー」の定義については、若干注意が必要です。
PUBLISHメッセージについては、サーバーがメッセージを中継する際にユーザープロパティの内容もそのまま伝達しなければならない旨が仕様に明記されています。 すなわち、PUBLISHのユーザープロパティについては、パブリッシャーとサブスクライバーの間のエンドツーエンドでやり取りができる情報となります。 このため、サーバー機能を提供するミドルウェアやサービスの仕様に関わらず、クライアントアプリケーションで勝手に仕様を決めて使用できます。 この意味で、PUBLISHメッセージにおける「ユーザー」とは、サーバーを利用する「クライアントアプリケーション」であると解釈できます。
PUBLISHメッセージのユーザープロパティについて記載されている 「3.3.2.3.7 ユーザープロパティ」 にも、 Non-normative commentとしてその旨が注記されています。
Non-normative comment
This property is intended to provide a means of transferring application layer name-value tags whose meaning and interpretation are known only by the application programs responsible for sending and receiving them.
一方で、PUBLISHメッセージ以外については、基本的にサーバーとクライアントのやり取りに使用するメッセージのため、サーバーとクライアントの間で仕様が合意されている必要があります。 想定される使用方法としては、サーバー機能を提供するミドルウェアやサービス事業者が、MQTT仕様に存在しない拡張仕様を付加価値として提供する場合が考えられます。 ミドルウェアやサービス事業者が拡張機能を実装してユーザープロパティ仕様を公開し、それに従ってクライアントが情報をやり取りすることによって拡張機能を利用できる、といった具合です。
この意味で、PUBLISH以外のメッセージにおける「ユーザー」とは、サーバー利用者としての「クライアントアプリケーション」というよりは、「プロトコルの利用者としての」サーバーやクライアントアプリケーションを指すものであると解釈できます。
Reason Code
★MQTT5で変更あり
以下のコントロールパケットには、Reason Code(理由コード)と呼ばれる1バイトの数値フィールドが含まれます。
- CONNACK
- PUBACK、PUBREC、PUBREL、PUBCOMP
- DISCONNECT
- AUTH
少し特殊なケースとして、以下のコントロールパケットでは、Variable HeaderではなくPayloadにResult Codeが格納されます。 これらのパケットでは複数のReason Codeを一度に返却する必要があるため、他のパケットとは異なる定義となっているようです。
- SUBACK、UNSUBACK
Reason Codeは、先立つコントロールパケット等によって行われた操作の結果を表すために使用されます。 数値ごとに異なる理由や結果が割り当てられていますが、値の範囲によってざっくりと成功か失敗かが分かるようになっています。
- 0x80未満: 正常に操作が完了したことを表し、通常は0が使用されます
- 0x80以上: 操作が失敗したことを表します
MQTT 3.1.1では、Return Codeというフィールドが各コントロールパケットごとにそれぞれ定義されていましたが、 MQTT 5.0ではこれを共通フィールドとして格上げして、より汎用的に扱うようになりました。
Reason Codeは種類が多いためここでのご紹介は割愛します。 詳細は、OASIS仕様書の「2.4 Reason Code」を参照してください。
Payload | ペイロード
MQTTのコントロールパケットには、Payloadを含むものと含まないものがあります。 必須/オプショナルの別は以下の通りで、下記以外のメッセージには含まれません。
- CONNECT、SUBSCRIBE、SUBACK、UNSUBSCRIBE、UNSUBACK は必須
- PUBLISH はオプショナル
それぞれのコントロールパケットにおけるPayloadの中身は、それぞれのコントロールパケットごとに詳細に定義されていてここには書ききれないので、 興味のある方は OASIS仕様書 の各パケットのセクションを参照してください。
おわりに
本記事では、MQTT 5.0の仕様を理解するための基礎となるコントロールパケットの構造と、MQTT5.0での変化についてご紹介しました。 特にPropertiesの追加によってこれまでメッセージごとに定義されていたメタデータの格納方法が統一されたことで、ぐっと理解がしやすくなったと思います。 また、User Propertiesの追加によりサードパーティで仕様の拡張が可能となったのも、今後のMQTTの可能性を広げる素晴らしい拡張のように思います。
今回の解説は以上となります。次回以降の連載にもご期待ください。