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 ]Willについて

Last updated at Posted at 2025-12-07

はじめに

Willについてみたあと、動作を確認しましょう。

Willについて

Willは遺言を意味します。Connect時に"遺言"となるPublishパケットを設定しておくことで、異常切断となった場合にサーバはそれがクライアントから送信されたのと同じ処理(=Publish)を行います。つまり、クライアントがDisconnectを送らずに接続が失われた状態になった場合にだけ実行されます。
例えば下記のような場合に実行されます。

  • Ping/Pongタイムアウト
  • TCP切断・TCP RST
  • OS強制終了
  • プロトコル違反
  • サーバ側リソース枯渇による切断

逆に異常ではない正常切断とはクライアントがDisconnectを送って切断することです。

 

実験

この実験では異常切断を意図的に発生させるとBrokerがWillをPublish することを確認します。

  • Connect時にWillを設定
  • Disconnectを呼ばずにOSのネットワークソケットをCloseして異常切断を作りだす
  • WillパケットがPublishされて受信

 [※1]ではpahoの内部ソケット参照が残っていると自動再接続などが走るため、明示的に無効化しています。

import paho.mqtt.client as mqtt
import threading
import time

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

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

def subscriber():
    sub = mqtt.Client(client_id="sub-client",
                      callback_api_version=mqtt.CallbackAPIVersion.VERSION2)
    sub.on_message = on_message
    sub.connect(BROKER, 1883, 60)

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

    sub.loop_forever()

# ======================
# Publisher(異常終了をシミュレート)
# ======================
def publisher_with_will():
    pub = mqtt.Client(client_id="pub-client",
                      callback_api_version=mqtt.CallbackAPIVersion.VERSION2)

    # Will 設定
    pub.will_set(WILL_TOPIC, "pub offline", qos=1, retain=False)
    pub.connect(BROKER, 1883, 10)

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

    print("💀 Publisher の TCP接続だけ異常終了させます(プロセスは生きてる)")

    # --- ここが重要:TCP接続だけ kill する ---
    sock = pub.socket()
    if sock:
        sock.close()        # TCP FIN (Broker からは異常終了扱い)
    pub._sock = None        # [※1] paho の内部参照も破壊
    pub.loop_stop()         # ネットワークループを停止する

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

    time.sleep(1)
    publisher_with_will()

    # Will を受信する時間を確保
    time.sleep(10)

[~/qiita/client]$uv run will.py 
[送信] Hello 0
[受信] test/topic: Hello 0
[送信] Hello 1
[受信] test/topic: Hello 1
[送信] Hello 2
[受信] test/topic: Hello 2
Publisher の TCP接続だけ異常終了させます
[受信] test/will: pub offline

TCP RSTが発生したあとにtest/willにPublishが発行されていることを確認できます。(実際には RST / FIN どちらになるかは環境や Broker 実装によりますが、どちらも “Disconnect なしで接続断” と判定されるかと思います)

スクリーンショット 2025-11-17 21.30.17.jpg

まとめ

Willについて確認し、実際に動作確認を行いました

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?