0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?

[MQTT] MQTT5 Reason Code "Disconnect with Will Message"

Last updated at Posted at 2025-12-18

はじめに

今回は狭いテーマですが、DisconnectのReasonのひとつである、"Disconnect with Will Message"について確認します。

Reason Code "Disconnect with Will Message"

MQTT3.1.1では異常切断のみWillが発火しましたが、正常切断でも発火してほしい場合があります。MQTT5では、DisconnectのReasonにDisconnectwith Will Message設定すると、異常切断ではなくてもWillを発火させることができます。

規格における、Disconnectwith Will Messageについての記述

The Client wishes to disconnect but requires that the Server also publishes its Will Message.

実践

今回はサーバ側にmosquittoを利用します。
クライアント側からDisconnectの際にReasonを設定してWillが発火することを確認します。

サーバ側 mosquito

下記を参考にdocker上でmqttブローカーを立ち上げる
https://qiita.com/kino15/items/0ef252b10100b2ee2f05

クライアント側 paho

import paho.mqtt.client as mqtt
import threading
import time
from paho.mqtt.packettypes import PacketTypes
from paho.mqtt.reasoncodes import ReasonCode

BROKER = "localhost"
TOPIC = "test/topic"
WILL_TOPIC = "test/will"


# ======================
# Subscriber
# ======================
def on_message(client, userdata, msg):
    print(f"[受信] {msg.topic}: {msg.payload.decode()}")


def on_subscribe(client, userdata, mid, granted_qos, properties=None):
    print(f"[SUBACK] mid={mid}, qos={granted_qos}")


def subscriber():
    sub = mqtt.Client(
        client_id="sub-client",
        protocol=mqtt.MQTTv5,
        callback_api_version=mqtt.CallbackAPIVersion.VERSION2
    )
    sub.on_message = on_message
    sub.on_subscribe = on_subscribe

    sub.connect(BROKER, 1883, 60)

    sub.subscribe(TOPIC)
    sub.subscribe(WILL_TOPIC)

    print("[Subscriber] Start")

    # ★ スレッドでループ開始
    sub.loop_start()

    # このまま待機
    while True:
        time.sleep(1)


# ======================
# Publisher
# ======================
def publisher_with_will_disconnect():
    pub = mqtt.Client(
        client_id="pub-client",
        protocol=mqtt.MQTTv5,
        callback_api_version=mqtt.CallbackAPIVersion.VERSION2
    )

    # Will 設定
    pub.will_set(
        WILL_TOPIC,
        "pub offline via disconnect-with-will",
        qos=1,
        retain=False
    )

    pub.connect(BROKER, 1883, 10)

    for i in range(3):
        msg = f"Hello {i}"
        print(f"[送信] {msg}")
        pub.publish(TOPIC, msg)
        time.sleep(1)

    print("\nPublisher が DISCONNECT with Will Message を送信します (0x04)\n")

    # ★ ReasonCode の value を直接書き換える方式
    rc = ReasonCode(PacketTypes.DISCONNECT)
    rc.value = 0x04    # Disconnect with Will Message

    pub.disconnect(reasoncode=rc)

    print("[Publisher] DISCONNECT(0x04) 送信完了")


# ======================
# メイン
# ======================
if __name__ == "__main__":
    t = threading.Thread(target=subscriber, daemon=True)
    t.start()

    time.sleep(1)
    publisher_with_will_disconnect()

    print("Will を受信するのを待っています...\n")
    time.sleep(10)

Terminal

~/qiita/client]$uv run disconnect_will.py 
[Subscriber] Start
[SUBACK] mid=1, qos=[ReasonCode(Suback, 'Granted QoS 0')]
[SUBACK] mid=2, qos=[ReasonCode(Suback, 'Granted QoS 0')]
[送信] Hello 0
[受信] test/topic: Hello 0
[送信] Hello 1
[受信] test/topic: Hello 1
[送信] Hello 2
[受信] test/topic: Hello 2

Publisher が DISCONNECT with Will Message を送信します (0x04)

[Publisher] DISCONNECT(0x04) 送信完了
Will を受信するのを待っています...

[受信] test/will: pub offline via disconnect-with-will
[~/qiita/client]$

まとめ

必ず切断で発火するようになり、便利そうです。

著作権情報

Copyright © OASIS Open 2014. All Rights Reserved.
Available at: https://docs.oasis-open.org/mqtt/mqtt/v3.1.1/os/mqtt-v3.1.1-os.html

Copyright © OASIS Open 2019. All Rights Reserved.
Available at: https://docs.oasis-open.org/mqtt/mqtt/v5.0/os/mqtt-v5.0-os.html

0
0
0

Register as a new user and use Qiita more conveniently

  1. You get articles that match your needs
  2. You can efficiently read back useful information
  3. You can use dark theme
What you can do with signing up
0
0

Delete article

Deleted articles cannot be recovered.

Draft of this article would be also deleted.

Are you sure you want to delete this article?