同じクライアントIDで接続した場合
MQTT3.1の仕様によると、
If a client with the same Client ID is already connected to the server, the "older" client must be disconnected by the server before completing the CONNECT flow of the new client.
とあり、古い接続は完了させて、あたらしいクライアントが有効になります。つまり同じクライアントIDで接続した場合には、先に接続していたクライアントが切断されてしまいます。
接続セッションの保存のリトライ
MQTTクライアントは、clean sessionを設定しない限り、セッション情報(Subscribeトピックや、QoS1や2の未送信メッセージなどを含む)は保持しておくとされています。
This includes continuing to store QoS 1 and QoS 2 messages for the subscribed topics so that they can be delivered when the client reconnects. The server must also maintain the state of in-flight messages being delivered at the point the connection is lost. This information must be kept until the client reconnects.
したがって、意図しないネットワークの切断が発生してもセッションを再開することができます。この仕様にしたがって、接続が切断した場合に自動で再接続要求をしてくれるクライアント実装があります。
複数のクライアントが同じクライアントIDでリトライ
以上を総合して、「クライアント1」と「クライアント2」が同じクライアントIDで接続した場合を考えてみます。
- 「クライアント1」が接続
- 「クライアント2」が接続(「クライアント1」は切断される)
- 「クライアント1」が再接続(「クライアント2」は切断される)
- 「クライアント2」が再接続(「クライアント1」は切断される)
※以降、3. と4.の繰り返し
このように、クライアント間で再接続によるセッションの奪い合いが発生することになります。
クライアントID重複が検出できない
現在の仕様では、古い接続の切断のみ仕様として定められいます。また、古い接続を切断したことを、新しく接続したクライアントに通知する手段もありません。(Connection Accepted:接続が受け付けられた のみ)
CONNACKのリターンコード
Enumeration | HEX | Meaning |
---|---|---|
0 | 0x00 | Connection Accepted |
1 | 0x01 | Connection Refused: unacceptable protocol version |
2 | 0x02 | Connection Refused: identifier rejected |
3 | 0x03 | Connection Refused: server unavailable |
4 | 0x04 | Connection Refused: bad user name or password |
5 | 0x05 | Connection Refused: not authorized |
6-255 | Reserved for future use |
したがって、接続を乗っ取ったことをクライアントは識別できないようです。クライアントからは「なぜかすぐに接続が切れる」としか認識できず、クライアントID重複の検出は難しいと思われます。
まとめ
クライアントIDは、重複しないようにルールを決めた上で利用するしかなさそうです。ユーザー名とパスワード認証の仕様はあるにもかかわらず、クライアントIDについては別途ルールを定めてコントロールしていくしかなさそうです。
もし、現行バージョンでのよい管理方法をご存知の方がいらっしゃいましたら情報を頂きたいです。
また、今後のバージョンでこの辺りが改善されることを望みます。
(既存クライアントIDの乗っ取りは同じユーザーからのみに制約できるオプションなど希望…)
※この投稿では端折ってますが、
Broker:mosquitto
Client:node.jsのmqttモジュール
で検証しました。