From 4b65ffc194ed424c06e98655f8e6b3bcd1abc140 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Korina=20=C5=A0imi=C4=8Devi=C4=87?= Date: Tue, 12 Mar 2024 13:17:39 +0100 Subject: [PATCH] Add a chapter on disconnection Summary: related to T12804 Reviewers: ivica Reviewed By: ivica Subscribers: miljen, iljazovic Differential Revision: https://repo.mireo.local/D28465 --- doc/qbk/00_main.qbk | 3 +- doc/qbk/05_optimising_communication.qbk | 2 +- doc/qbk/06_disconnecting_the_client.qbk | 119 ++++++++++++++++++++++++ 3 files changed, 122 insertions(+), 2 deletions(-) create mode 100644 doc/qbk/06_disconnecting_the_client.qbk diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk index 5eab1d0..81e7a5f 100644 --- a/doc/qbk/00_main.qbk +++ b/doc/qbk/00_main.qbk @@ -42,6 +42,7 @@ [def __Asio__ [@boost:/libs/asio/index.html Boost.Asio]] [def __Beast__ [@boost:/libs/beast/index.html Boost.Beast]] [def __ASIO_PER_OP_CANCELLATION__ [@boost:/doc/html/boost_asio/overview/core/cancellation.html Per-Operation Cancellation]] +[def __IOC__ [@boost:doc/html/boost_asio/reference/io_context.html `boost::asio::io_context`]] [def __POST__ [@boost:doc/html/boost_asio/reference/post.html `boost::asio::post`]] [def __CO_SPAWN__ [@boost:/doc/html/boost_asio/reference/co_spawn.html `boost::asio::co_spawn`]] [def __USE_AWAITABLE__ [@boost:/doc/html/boost_asio/reference/use_awaitable.html `boost::asio::use_awaitable`]] @@ -114,7 +115,7 @@ [include 03_auto_reconnect.qbk] [include 04_maintaining_a_stable_connection.qbk] [include 05_optimising_communication.qbk] - +[include 06_disconnecting_the_client.qbk] [include 10_examples.qbk] diff --git a/doc/qbk/05_optimising_communication.qbk b/doc/qbk/05_optimising_communication.qbk index 08926a5..f8950a5 100644 --- a/doc/qbk/05_optimising_communication.qbk +++ b/doc/qbk/05_optimising_communication.qbk @@ -69,7 +69,7 @@ A Broker can set this value to limit the number of simultaneous QoS > 0 messages potentially causing QoS 0 messages to be transmitted ahead of QoS > 0 messages in the delivery order. - The __DISCONNECT__ packet is sent *in a single TCP packet before any other packets* in the queue. -[/ TODO: reference to the disconnect chapter] +See [link async_mqtt5.disconnecting_the_client Disconnecting the client] for more information about disconnecting. [endsect] [/packet_ordering] diff --git a/doc/qbk/06_disconnecting_the_client.qbk b/doc/qbk/06_disconnecting_the_client.qbk new file mode 100644 index 0000000..e430c55 --- /dev/null +++ b/doc/qbk/06_disconnecting_the_client.qbk @@ -0,0 +1,119 @@ +[section:disconnecting_the_client Disconnecting the Client] +[nochunk] + +The __Client__ remains active until it is either destroyed or explicitly stopped. +In idle mode, the __Client__ periodically sends __PINGREQ__ to the Broker to maintain a stable connection. + +The proper way to stop the __Client__ is by calling either [refmem mqtt_client cancel] or [refmem mqtt_client async_disconnect]. +Invoking [refmem mqtt_client cancel] results in the __Client__ closing the connection to the Broker and cancelling all outstanding +asynchronous operations. +On the other hand, [refmem mqtt_client async_disconnect] will first attempt to send a __DISCONNECT__ packet +[footnote The __Client__ will attempt to send the __DISCONNECT__ packet for `5 seconds`. Regardless of the outcome, the connection will be closed.] +to the Broker in order to notify it about the reason for disconnection, +then close the connection and cancel all outstanding asynchronous operations (equal effect as [refmem mqtt_client cancel]). + +[important Regardless of the method used to stop the __Client__, it is recommended to ensure that all the previous asynchronous operations are +completed. Otherwise, they *will be cancelled*.] + +Invoking [refmem mqtt_client cancel] or [refmem mqtt_client async_disconnect] will result in a clean and graceful shutdown process. +This ensures that all resources are properly released and all asynchronous operations are +completed [footnote All outstanding operations will complete with error code `boost::asio::error::operation_aborted`.]. +Consequently, the execution context (__IOC__) will stop due to a lack of work. + +[note The __Client__'s destructor will also call [refmem mqtt_client cancel]. ] + +The following code snippet will showcase a scenario of disconnecting the __Client__ and its interaction with other +asynchronous operations. + +[heading Example: immediate disconnection and its impact on outstanding asynchronous operations] + +The following code snippet is an example of publishing a "Hello World!" message to the Broker with QoS `0`, +followed by the request to disconnect the __Client__. + +``` +int main() { + boost::asio::io_context ioc; + + async_mqtt5::mqtt_client client(ioc); + client.brokers("", 1883) + .async_run(boost::asio::detached); + + client.async_publish( + "", "Hello world!", + async_mqtt5::retain_e::no, async_mqtt5::publish_props {}, + [](async_mqtt5::error_code ec) { + std::cout << ec.message() << std::endl; + } + ); + + client.async_disconnect(boost::asio::detached); + + ioc.run(); +} +``` + +Suppose the Broker is available and the __Client__ can successfully connect to it, then the following +order of events will unfold: + +# The Client will successfully establish a connection to the Broker. +# The Client will send a __DISCONNECT__ packet with Reason Code `0x00` (`Normal Disconnection`). + +It is important to note that the __PUBLISH__ packet containing the "Hello World!" message will not be transmitted. +As outlined in the `Packet Ordering` in [link async_mqtt5.optimising_communication Optimising communication] section, +[refmem mqtt_client async_publish] and [refmem mqtt_client async_disconnect] will place their corresponding +packets in the queue. However, __DISCONNECT__ packets are prioritised and sent exclusively, ahead of other queued packets. +Therefore, the connection will terminate immediately. + +If the __Client__ cannot establish a connection to the Broker, +it will be stopped after `5 seconds`, which is the amount of time the it will spend +trying to send the __DISCONNECT__ packet to the Broker before quitting. +This timeout mechanism ensures that the __Client__ does not indefinitely wait to disconnect, +preserving resources and maintaining efficient operation. + +In this case, the proper way to disconnect would be to call [refmem mqtt_client async_disconnect] after the +[refmem mqtt_client async_publish] has been completed. + +``` + client.async_publish( + "", "Hello world!", + async_mqtt5::retain_e::no, async_mqtt5::publish_props {}, + [&client](async_mqtt5::error_code ec) { + std::cout << ec.message() << std::endl; + client.async_disconnect(boost::asio::detached); + } + ); +``` + +[section:reusing_the_client Restarting the Client after disconnection] + +Once the __Client__ has been successfully stopped, reactivating it is straightforward and requires invoking [refmem mqtt_client async_run]. +This method can be called right after initiating [refmem mqtt_client async_disconnect], without waiting for it to complete. + +The __Client__ is configurable again in the interval between stopping and restarting. +See `Customising your MQTT connection` in [link async_mqtt5.configuring_the_client Configuring the Client] +for more information. + +``` +int main() { + boost::asio::io_context ioc; + + async_mqtt5::mqtt_client client(ioc); + client.brokers("", 1883) + .async_run(boost::asio::detached); + + client.async_disconnect(boost::asio::detached); + + // The Client can be reconfigured again. + client.connect_property(async_mqtt5::prop::session_expiry_interval, 120) + .keep_alive(30) + .async_run(boost::asio::detached); // Restart the Client again. + + // Use the Client... + + ioc.run(); +} +``` + +[endsect] [/reusing_the_client] + +[endsect] [/disconnecting_the_client]