MQTTバージョン5の概要 2018年版

<font color="Red">2018/12/11更新 COS(Candidate OASIS Standard)が11/16に発行されましたので、予想スケジュールをアップデートしました。
2019/3/10更新 3/8に、MQTTv5がついにOASIS標準になりました。
2019/3/22更新 MQTTv5の正式公開日が3/7となりました。
</font>

[旧連載](https://qiita.com/toast-uz/items/bd21ce2995f313521c56)から約1年が経ち、MQTTバージョン5の標準化も完了が近づいて来ました。本稿では、最新の標準ドラフトに基づいて、MQTTバージョン5の要点解説をします。

なお、本稿は、v.3.1.1ベースでのMQTTプロトコルの知識がある方を対象として、そこからの差分を中心に解説しています。[旧連載](https://qiita.com/toast-uz/items/bd21ce2995f313521c56)では、若干記述が古くて不正確な部分もありますが、一部はより深い解説をしていますので、適宜そちらも参照ください。

#1. 今後の予想スケジュール

##1.1. 標準化完了は2018年5月見込み <font color="Red">→2019年2月見込み→2019年3月7日完了</font>

現時点（2018年1月4日）は、CS（Committee Specification）01が発行されたところです。CS発行から標準化完了のOS（OASIS Standard）まで、約3か月強かかる予定ですので、若干の遅れも考慮に入れて、標準化完了は2018年5月と予想します。[旧連載](https://qiita.com/toast-uz/items/bd21ce2995f313521c56)では2017年末と予想しましたが、約5か月遅れています。

<font color="Red">CS発行からCOS(Candidate OASIS Standard)発行まで、なんと10か月以上を要しました。CSへのコメント反映であるCS2が５月に発行されましたが、その後、OASISの標準化ルールである「COS発行には、3つ以上の独立したstatement of use（その標準がインプリメント可能であることを証明する実装）が必要である」のため、statement of useが3つ揃うのを待っていました。ついに、HiveMQ、VerneMQ、Eclipse Pahoが揃ったため、COSにやっと移行できました。
※CS/COS/OSといった標準化タスクの説明は[旧連載](https://qiita.com/toast-uz/items/bd21ce2995f313521c56)を参照ください。
</font>

なお、OASIS標準は、いわゆる「コミュニティー標準」であり、国際標準ではありません。前の版のMQTT3.1.1は、OASIS標準化から約1年半経過して、国際標準であるISO/IEC標準になりました。

##1.2. 本格的利用が開始されるのは2019年度

現時点で、バージョン5の実装は、IBMが中心となって推進しているオープンソースであるEclipse Pahoが先行して着手しはじめています。Pahoでは各種言語向けのクライアントライブラリと、テストコードを提供しています。<font color="Red">その後の状況は前節に記載の通りです。</font>

しかし、Eclipse Mosquittoなどのブローカーでの実装はまだ始まっていません。バージョン5の更新範囲は膨大ですので、少しずつβ版での実装が進み、主要ブローカーでの実装が完了するのは、2018年度後半になると思われます。<font color="Red">この予想は的中していました。よって以降の予想時期は変えません。2019/3/10追記 主要ブローカーで実装が進み、Mosquittoでもβ実装となっています。</font>

よって、バージョン5が本格的に利用開始されるのは2019年度になると予想します。

##1.3. メジャーに使われるようになるのは2021年度以降

新規のMQTTプロトコル実装が当然のごとくバージョン5を前提とするような「メジャー化」には、さらに2年を要すると予想します。

#2. パケットフォーマット新旧比較

旧バージョンである3.1.1と比較しながら、パケットフォーマットを解説します。

共通で、以下のデータ型を利用しています。

|本稿での略記|規格での型名|意味|
|:--|:--|:--|
|Byte|Bits|1バイト数値または8ビットのフラグ|
|2-Bytes|Two Byte Integer|2バイト符号なし整数|
|4-Bytes|Four Byte Integer|4バイト符号なし整数|
|String|UTF-8 Encoded String|2バイト符号なし整数での文字列長、およびUTF-8文字列|
|Variable|Variable Byte Integer|可変長整数（0〜268,435,455）、整数の大きさによって1〜4バイトの可変長にエンコードされる|
|Binary|Binary Data|2バイト符号なし整数での長さ、およびバイナリ列|
|String Pair|UTF-8 String Pair|連続する2つのString|
|・・・||0個以上の繰り返しを示す|

##2.1. CONNECT

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/e8c98396-30d1-8ab4-2e3a-bcd6fee42122.png)

+ バージョン番号の内部表現が4から5に上がっています。
+ バージョン5から導入されたPropertyが、2箇所（CONNECTそのもののPropertyと、WILL Property）追加されています。
+ Connect Flag内の、Clean Sessionフラグが、Clean Startフラグに名称変更されています。Clean Sessionの役割が、Clean Startと、PropertyのSession Expiry Intervalの双方に分散されたからです。

##2.2. CONNACK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/598b465b-db61-d5f7-4414-6531b30ebcf0.png)

+ 従来のReturn Codeが拡張され、Reason Codeに名称変更しました。
+ Propertyが追加されました。

##2.3. PUBLISH

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/c1e7da86-8f51-ccc5-0a58-140b86079ab9.png)

+ Propertyの追加が、唯一の変更点です。

##2.4. PUBACK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/e5d429cb-cae9-20f9-c67e-eb5ee5f8b2ca.png)

+ Reason Codeがオプションで追加されました（Remaining Length=2なら不要で、Reason Code=0x00(Success)を意味します）。
+ Propertyがオプションで追加されました（Remaining Lengthが4未満なら不要）。

##2.5. PUBREC

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/a81a940a-ed8d-e2d4-0d72-820590ddccb9.png)

+ Reason Codeがオプションで追加されました（Remaining Length=2なら不要で、Reason Code=0x00(Success)を意味します）。
+ Propertyがオプションで追加されました（Remaining Lengthが4未満なら不要）。

##2.6. PUBREL

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/2365cb3e-8a5d-cffa-e39d-5f606c0b0c0d.png)

+ Reason Codeがオプションで追加されました（Remaining Length=2なら不要で、Reason Code=0x00(Success)を意味します）。
+ Propertyがオプションで追加されました（Remaining Lengthが4未満なら不要）。

##2.7. PUBCOMP

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/6cde9cff-848f-31ae-db43-0a31b3547717.png)

+ Reason Codeがオプションで追加されました（Remaining Length=2なら不要で、Reason Code=0x00(Success)を意味します）。
+ Propertyがオプションで追加されました（Remaining Lengthが4未満なら不要）。

##2.8. SUBSCRIBE

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/2113228e-5b8e-7708-a99c-dc1b95423548.png)

+ Propertyが追加されました。
+ 従来のRequested QoSは、様々なフラグを追加することで拡張され、Subscription Optionに名称変更しました。（下図参照）

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/143d67d8-5ac6-c3eb-207e-6be06b06cf9b.png)

+ Subscription Optionに追加されたフラグの説明を、以下に示します。
  + NLとRAPは、Bridge型のブローカー実装に役立つように追加されました。
     + NL(No Local)オプションが1の場合、同一のClient IDからのメッセージ転送を禁止します。
     + RAP(Retain As Published)オプションが1の場合、コネクション確立済SUBSCRIBEに転送されるメッセージについて、転送前メッセージのRetainオプションを転送後も維持します。このオプションが0の場合、転送後のRetainオプションは一律0にセットされます。
  + RH(Retain Handling)オプションは以下の通りです。
     + 0の場合、SUBSCRIBE開始時点で全てのRetainメッセージが転送されます。
     + 1の場合、同一セッション内での新規のSUBSCIBEの場合だけRetainメッセージが転送されます。同一セッションの過去のコネクションで既にSUBSCRIBE済の場合はRetainメッセージは転送されません。（同一セッションの過去のコネクションにおいて受信したRetainメッセージを、再度受信しないですみます）
     + 2の場合、全てのRetainメッセージは転送されません。いずれの場合も、SUBSCRIBE中に受信したメッセージはRetainかどうかにかかわらず転送されます。

##2.9. SUBACK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/172b5cb2-00c4-b45d-3d82-e7877de4c6be.png)

+ Propertyが追加されました。
+ 従来のReturn Codeが拡張され、Reason Codeに名称変更しました。

##2.10. UNSUBSCRIBE

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/aa3d46e8-78e6-edb9-66ce-433ea1acfb17.png)

+ Propertyの追加が、唯一の変更点です。

##2.11. UNSUBACK

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/99fb854d-4631-511d-744d-d6c1000c6822.png)

+ Propertyが追加されました。
+ Payloadが必須となり、Reason Codeのリストを返すようになりました。

##2.12. PINGREQ

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/e908262b-2845-efb8-0d96-f5c30631154b.png)

+ 変更ありません。

##2.13. PINGRESP

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/3c163494-146d-ccee-3aa4-79a70eeebf5b.png)

+ 変更ありません。

##2.14. DISCONNECT

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/75c90d8c-d2df-22f6-f1fd-5ad176db420b.png)

+ Variable Headerがオプションで記述可能（Remaining Length=0なら不要）となり、Reason Codeを返すようになりました。Remaining Length=0の場合、Reason Code=0x00(Normal disconnection)となります。
+ Propertyがオプションで追加されました（Remaining Lengthが2未満なら不要）。
+ バージョン5からの大きな追加として、ブローカーからDISCONNECTが送信可能になりました。

##2.15. AUTH

![image.png](https://qiita-image-store.s3.amazonaws.com/0/156600/ccd92c5c-c0e9-36c7-8921-06978619b515.png)

+ バージョン5で新たに追加されたコントロールパケットです。CONNECTのPropertyでAuthentication Methodを指定すると、CONACKパケットを受け取るまでの間は、オプションでAUTHパケットをやりとりすることが可能になります。
+ Remaining Length=0の場合は、Reason Code=0x00(Success)かつPropertyなしを意味します。

#3. 拡張されたReason Code

従来のReturn Codeが拡張され、バージョン5では、Reason Codeに名称変更するとともに、コマンドに依存しない一意（0x00を除く）の数値に再割り当てされました。正常を表すコードについてはv3.1.1からの互換性を維持するとともに、異常を表すコードは0x80以上にまとめられました。

一覧で解説します。

v3.1.1 CONNACK Return Code:

|数値|名称|適用コマンド|
|:--|:--|:--|
|0x00|Connection Accepted|CONNACK|
|0x01|Connection Refused, unacceptable protocol version|CONNACK|
|0x02|Connection Refused, identifier rejected|CONNACK|
|0x03|Connection Refused, Server unavailable|CONNACK|
|0x04|Connection Refused, bad user name or password|CONNACK|
|0x05|Connection Refused, not authorized|CONNACK|

v3.1.1 SUBACK Return Code:

|数値|名称|適用コマンド|
|:--|:--|:--|
|0x00|Success - Maximum QoS 0|SUBACK|
|0x01|Success - Maximum QoS 1|SUBACK|
|0x02|Success - Maximum QoS 2|SUBACK|
|0x80|Failure|SUBACK|

v5 Reson Code:

|数値|名称|適用コマンド|
|:--|:--|:--|
|0x00|Success|CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, UNSUBACK, AUTH|
|0x00|Normal disconnection|DISCONNECT|
|0x00|Granted QoS 0|SUBACK|
|0x01|Granted QoS 1|SUBACK|
|0x02|Granted QoS 2|SUBACK|
|0x04|Disconnect with Will Message|DISCONNECT|
|0x10|No matching subscribers|PUBACK, PUBREC|
|0x11|No subscription existed|UNSUBACK|
|0x18|Continue authentication|AUTH|
|0x19|Re-authenticate|AUTH|
|0x80|Unspecified error|CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT|
|0x81|Malformed Packet|CONNACK, DISCONNECT|
|0x82|Protocol Error|CONNACK, DISCONNECT|
|0x83|Implementation specific error|CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT|
|0x84|Unsupported Protocol Version|CONNACK|
|0x85|Client Identifier not valid|CONNACK|
|0x86|Bad User Name or Password|CONNACK|
|0x87|Not authorized|CONNACK, PUBACK, PUBREC, SUBACK, UNSUBACK, DISCONNECT|
|0x88|Server unavailable|CONNACK|
|0x89|Server busy|CONNACK, DISCONNECT|
|0x8A|Banned|CONNACK|
|0x8B|Server shutting down|DISCONNECT|
|0x8C|Bad authentication method|CONNACK, DISCONNECT|
|0x8D|Keep Alive timeout|DISCONNECT|
|0x8E|Session taken over|DISCONNECT|
|0x8F|Topic Filter invalid|SUBACK, UNSUBACK, DISCONNECT|
|0x90|Topic Name invalid|CONNACK, PUBACK, PUBREC, DISCONNECT|
|0x91|Packet Identifier in use|PUBACK, PUBREC, SUBACK, UNSUBACK|
|0x92|Packet Identifier not found|PUBREL, PUBCOMP|
|0x93|Receive Maximum exceeded|DISCONNECT|
|0x94|Topic Alias invalid|DISCONNECT|
|0x95|Packet too large|CONNACK, DISCONNECT|
|0x96|Message rate too high|DISCONNECT|
|0x97|Quota exceeded|CONNACK, PUBACK, PUBREC, SUBACK, DISCONNECT|
|0x98|Administrative action|DISCONNECT|
|0x99|Payload format invalid|CONNACK, PUBACK, PUBREC, DISCONNECT|
|0x9A|Retain not supported|CONNACK, DISCONNECT|
|0x9B|QoS not supported|CONNACK, DISCONNECT|
|0x9C|Use another server|CONNACK, DISCONNECT|
|0x9D|Server moved|CONNACK, DISCONNECT|
|0x9E|Shared Subscriptions not supported|SUBACK, DISCONNECT|
|0x9F|Connection rate exceeded|CONNACK, DISCONNECT|
|0xA0|Maximum connect time|DISCONNECT|
|0xA1|Subscription Identifiers not supported|SUBACK, DISCONNECT|
|0xA2|Wildcard Subscriptions not supported|SUBACK, DISCONNECT|


#4. 新しく規定されたProperty

バージョン5から、新しくPropertyが規定されました。一覧で概説するとともに、いくつかについては詳細解説します。

##4.1. 一覧

|数値|名称|意味概説|データ型|適用コマンド|
|:--|:--|:--|:--|:--|
|0x01|Payload Format Indicator|ペイロードフォーマット識別子（0:Binary、1:String）|Byte|PUBLISH, Will|
|0x02|Message Expiry Interval|メッセージの最大有効期間（秒）、メッセージ転送時に値を更新する|4-Bytes|PUBLISH, Will|
|0x03|Content Type|ペイロートフォーマット補足情報（例えばMIME Typeなど）|String|PUBLISH, Will|
|0x08|Response Topic|Request-Responseの返信Topic|String|PUBLISH, Will|
|0x09|Correlation Data|Request-Responseの相互共有情報（トランザクションIDなど）|Binary|PUBLISH, Will|
|0x0B|Subscription Identifier|Subscription ID|Variable |PUBLISH, SUBSCRIBE|
|0x11|Session Expiry Interval|セッションの最大維持期間（秒）|4-Bytes|CONNECT, CONNACK, DISCONNECT|
|0x12|Assigned Client Identifier|Client IDが指定されていない場合に、プローカーからClient IDを強制指定|String|CONNACK|
|0x13|Server Keep Alive|ブローカーからKeep Alive値（秒）を強制上書き|2-Bytes|CONNACK|
|0x15|Authentication Method|拡張認証の方式名|String|CONNECT, CONNACK, AUTH|
|0x16|Authentication Data|拡張認証の任意の補足データ|Binary|CONNECT, CONNACK, AUTH|
|0x17|Request Problem Information|詳細エラー情報の許可|Byte|CONNECT|
|0x18|Will Delay Interval|WILLの送信待機時間（秒）|4-Bytes|Will|
|0x19|Request Response Information|Request-Response参照情報の要求|Byte|CONNECT|
|0x1A|Response Information|Request-Response参照情報|String|CONNACK|
|0x1C|Server Reference|リダイレクト情報|String|CONNACK, DISCONNECT|
|0x1F|Reason String|詳細エラー情報等の任意の補足文字列|String|CONNACK, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBACK, UNSUBACK, DISCONNECT, AUTH|
|0x21|Receive Maximum|QoS1/2のメッセージをレスポンスを待たずに同時送受信できる最大数（双方向それぞれ指定可能）|2-Bytes|CONNECT, CONNACK|
|0x22|Topic Alias Maximum|Topic Aliasの最大値（双方向それぞれ指定可能）|2-Bytes|CONNECT, CONNACK|
|0x23|Topic Alias|今回指定のTopic Aliasの値、Topic名をあわせて指定した場合はTopic Alias値の新規アサインもしくは再アサインを意味する|2-Bytes|PUBLISH|
|0x24|Maximum QoS|最大QoS|Byte|CONNACK|
|0x25|Retain Available|RETAINの許可|Byte|CONNACK|
|0x26|User Property|任意のユーザ定義プロパティ名と値文字列のペア（複数記述可能）|String Pair|CONNECT, CONNACK, PUBLISH, Will, PUBACK, PUBREC, PUBREL, PUBCOMP, SUBSCRIBE, SUBACK, UNSUBSCRIBE, UNSUBACK, DISCONNECT, AUTH|
|0x27|Maximum Packet Size|送受信可能な最大パケット長（双方向それぞれ指定可能）|4-Bytes|CONNECT, CONNACK|
|0x28|Wildcard Subscription Available|ワイルドカードの許可|Byte|CONNACK|
|0x29|Subscription Identifier Available|Subscription IDの許可|Byte|CONNACK|
|0x2A|Shared Subscription Available|共有Subscriptionの許可|Byte|CONNACK|

##4.2. 詳細解説

###4.2.1. Request-Response

MQTTでは困難であったエンドツーエンドでのRequest-Responseパターンを実現する。

+ 応答側はしかるべき処理をした後、Response Topic宛てに応答を返信する。要求側は一般的にResponse TopicをSUBSCRIBEして待ち受ける。
+ 要求メッセージにCorrelation Dataが含まれている場合、応答側クライアントはその内容をコピーして、応答メッセージのCorrelation Dataに書き込むことで、要求と応答の関連付けが可能になる。これは、シーケンス番号などのトランザクションIDに利用可能である。
+ 要求側は任意の方法で応答トピックを決定することが可能。多くの場合、応答トピックはクライアントごとに一意なものになる。
+ オプションとして、CONNECT時にRequest Response Informationプロパティをセットして、サーバからクライアントに対応しトピックフリーなグローバルな応答トピック（クライアント個々のルートトピック等）を指定することで、個々のクライアントに応答トピックを定義せずに済む。
+ 関連するPropertyは、0x08(Response Topic)、0x09(Correlation Data)、0x19(Request Response Information)、0x1A(Response Information)。

###4.2.2. Subscription ID

クライアントは、メッセージがどのSUBSCRIBEに一致したのかを簡単に判別することが可能で、それに基づくアクションにつなげることができる。

+ SUBSCRIBEトピックごとにオプション付与する。
+ セッション内で一意な値をとるが、新たな値で上書きすることも可能。クライアント側としては、セッションを超えて一意にする実装することが有用な場合が多い。
+ メッセージがどのSUBSCRIBEに一致したのかが、PUBLISHにおけるSubscription IDのリストで表現される。
+ 関連するPropertyは、0x0B(Subscription Identifier)、0x29(Subscription Identifier Available)。

###4.2.3. 拡張認証

単純なBasic認証よりも強固な認証を利用することができる。

+ クライアントがCONNECTでAuth Methodを指定することにより、拡張認証が起動される。
+ CONNECTとCONNACKの間でAUTHパケットを交換することにより、チャレンジレスポンスなどが可能になる。
+ 関連するPropertyは、0x15(Authentication Method)、0x16(Authentication Data)。

###4.2.4. Topic Alias

Topicをエイリアス値で代替することで、メッセージサイズを短縮することができる。

+ Topic Aliasと有効なトピックの組み合わせにより、Topic Aliasを設定または再設定できる。
+ Topic Aliasを設定または再設定すると、同一ネットワークコネクションの次のリクエストから、Topic Aliasと0バイトのトピックの組み合わせにより、同じトピックを指定したものとみなす。
+ 関連するPropertyは、0x22(Topic Alias Maximum)、0x23(Topic Alias)。

###4.2.5. 共有Subscription

コンシューマとなるクライアントを複数並列させて負荷分散構成をとることができる。

+ 共有サブスクリプションでは、ただ1つのセッションにのみメッセージがPUBLISHされる。どのセッションにメッセージをPUBLISHするかは、ブローカーの実装依存。
+ トピック形式は、$share/{ShareName}/{filter} で表現される。
+ 関連するPropertyは、0x2A(Shared Subscription Available)。