diff --git a/README.md b/README.md index 0c25620..4f987e7 100644 --- a/README.md +++ b/README.md @@ -5,7 +5,7 @@ Branch | Windows/Linux Build | Coverage | Documentation | -------|---------------------|----------|---------------| [`master`](https://github.com/mireo/async-mqtt5/tree/master) | [![build status](https://github.com/mireo/async-mqtt5/actions/workflows/ci.yml/badge.svg?branch=master)](https://github.com/mireo/async-mqtt5/actions/workflows/ci.yml) | [![codecov](https://codecov.io/gh/mireo/async-mqtt5/branch/master/graph/badge.svg)](https://codecov.io/gh/mireo/async-mqtt5/branch/master) | [Documentation](https://spacetime.mireo.com/async-mqtt5/) -Async.MQTT5 is a professional, industrial-grade C++17 client built on [Boost.Asio](https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio.html). This client is designed for publishing or receiving messages from an MQTT 5.0 compatible broker. Async.MQTT5 represents a comprehensive implementation of the MQTT 5.0 protocol standard, offering full support for publishing or receiving messages with QoS 0, 1, and 2. +Async.MQTT5 is a professional, industrial-grade C++17 client built on [Boost.Asio](https://www.boost.org/doc/libs/1_82_0/doc/html/boost_asio.html). This Client is designed for publishing or receiving messages from an MQTT 5.0 compatible Broker. Async.MQTT5 represents a comprehensive implementation of the MQTT 5.0 protocol standard, offering full support for publishing or receiving messages with QoS 0, 1, and 2. Our clear intention is to include the Async.MQTT5 library into [Boost](https://www.boost.org/). We are actively working on it. @@ -13,9 +13,9 @@ Motivation --------- The [MQTT](https://mqtt.org/) protocol is widely utilised for communication in various real-world scenarios, primarily serving as a reliable communication protocol for data transfer to and from IoT devices. While the MQTT protocol itself is relatively straightforward, integrating it into an application can be complex, especially due to the challenging implementation of message retransmission after a disconnect/reconnect sequence. -The aim of Async.MQTT5 is to provide a very simple asynchronous C++ interface for application developers. The internal client's implementation manages network and MQTT protocol details. Notably, the client does not expose connect functions (nor asynchronous connect functions); instead, network connectivity, MQTT handshake, and message retransmission are automatically handled within the client. +The aim of Async.MQTT5 is to provide a very simple asynchronous C++ interface for application developers. The internal Client's implementation manages network and MQTT protocol details. Notably, the Client does not expose connect functions (nor asynchronous connect functions); instead, network connectivity, MQTT handshake, and message retransmission are automatically handled within the Client. -The Async.MQTT5 interface aligns seamlessly with the Boost.Asio asynchronous model. The client's asynchronous functions are compatible with all completion tokens supported by Boost.Asio. +The Async.MQTT5 interface aligns seamlessly with the Boost.Asio asynchronous model. The Client's asynchronous functions are compatible with all completion tokens supported by Boost.Asio. Features --------- @@ -24,7 +24,7 @@ The library attempts to embody this belief with a range of key features designed - **Complete TCP, TLS/SSL, and WebSocket support** - **User-focused simplicity**: Providing an interface that is as simple as possible without compromising functionality. -- **Prioritized efficiency**: Utilising network and memory resources as efficiently as possible. +- **Prioritised efficiency**: Utilising network and memory resources as efficiently as possible. - **Minimal memory footprint**: Ensuring optimal performance in resource-constrained environments typical of IoT devices. - **Automatic reconnect**: Automatically attempt to re-establish a connection in the event of a disconnection. - **Fully Boost.Asio compliant**: The interfaces and implementation strategies are built upon the foundations of Boost.Asio. Boost.Asio and Boost.Beast users will have no issues understanding and integrating Async.MQTT5. Furthermore, Async.MQTT5 integrates well with any other library within the Boost.Asio ecosystem. @@ -94,11 +94,11 @@ When to use - Your application uses Boost.Asio and requires integrating a MQTT Client. - You require asynchronous access to an MQTT Broker. - You are developing a higher-level component that requires a connection to an MQTT Broker. -- You require a dependable and resilient MQTT Client that can automatically manage all network-related issues. +- You require a dependable and resilient MQTT Client to manage all network-related issues automatically. It may not be suitable for you if: - You solely require synchronous access to an MQTT Broker. -- The MQTT Broker you are connecting to does not support the MQTT 5 version. +- The MQTT Broker you connect to does not support the MQTT 5 version. Requirements diff --git a/doc/Jamfile b/doc/Jamfile index ec0b936..1dd24eb 100644 --- a/doc/Jamfile +++ b/doc/Jamfile @@ -32,6 +32,7 @@ make xml/index.xml # additional dependencies ../include/async_mqtt5/error.hpp + ../include/async_mqtt5/reason_codes.hpp ../include/async_mqtt5/types.hpp ../include/async_mqtt5/mqtt_client.hpp : @@ -129,7 +130,8 @@ boostbook async_mqtt5 chapter.autolabel=1 chunk.section.depth=8 chunk.first.sections=1 - generate.toc="" + toc.max.depth=2 + generate.toc="chapter toc,title section nop reference nop part toc" html.stylesheet=boostbook.css : stylesheets diff --git a/doc/qbk/00_main.qbk b/doc/qbk/00_main.qbk index 75f135e..07ae2ce 100644 --- a/doc/qbk/00_main.qbk +++ b/doc/qbk/00_main.qbk @@ -20,6 +20,7 @@ [template indexterm1[term1] ''''''[term1]''''''] [template indexterm2[term1 term2] ''''''[term1]''''''[term2]''''''] +[template ghreflink[path text] [@https://github.com/mireo/async-mqtt5/[path] [text]]] [template reflink2[id text][link async_mqtt5.ref.[id] [^[text]]]] [template reflink[id][reflink2 [id] [id]]] [template refmem[class mem][reflink2 [class].[mem] [class]::[mem]]] @@ -108,26 +109,15 @@ [def __REASON_CODES__ [reflink2 Reason_codes `Reason Codes`]] [def __ERROR_HANDLING__ [reflink2 Error_handling `Error handling`]] -[def __EXAMPLE_CALLBACK__ [link async_mqtt5.examples.callbacks Async functions with callbacks]] -[def __EXAMPLE_COROUTINE__ [link async_mqtt5.examples.cpp20_coroutines Async functions with C++20 coroutines]] -[def __EXAMPLE_FUTURE__ [link async_mqtt5.examples.futures Async functions with futures]] - [include 01_intro.qbk] -[include 02_examples.qbk] +[include 02_configuring_the_client.qbk] +[include 03_examples.qbk] +[include examples/Examples.qbk] + +[section:ref Reference] +[xinclude reference/quickref.xml] +[block''''''] [include reference/reference.qbk] - -[h3 Table of Contents] -* [link async_mqtt5.intro Introduction] -* [link async_mqtt5.examples Examples] - * [link async_mqtt5.examples.publisher The publisher] - * [link async_mqtt5.examples.receiver The receiver] - * [link async_mqtt5.examples.network_connection Establishing a network connection with different protocols] - * [link async_mqtt5.examples.network_connection.tcp_ip_connection TCP/IP connection] - * [link async_mqtt5.examples.network_connection.tls_ssl_connection TLS/SSL connection] - * [link async_mqtt5.examples.network_connection.websocket_connection WebSocket connection] - * [link async_mqtt5.examples.completion_tokens Completion tokens] - * [link async_mqtt5.examples.callbacks Async functions with callbacks] - * [link async_mqtt5.examples.cpp20_coroutines Async functions with C++20 coroutines] - * [link async_mqtt5.examples.futures Async functions with futures] -* [link async_mqtt5.ref Reference] +[block''''''] +[endsect] diff --git a/doc/qbk/01_intro.qbk b/doc/qbk/01_intro.qbk index 07ab59a..3b5bad7 100644 --- a/doc/qbk/01_intro.qbk +++ b/doc/qbk/01_intro.qbk @@ -2,7 +2,7 @@ [nochunk] __Self__ is a C++17 client built on __Asio__. -This client is designed for publishing or receiving messages from an MQTT 5.0 compatible broker. +This client is designed for publishing or receiving messages from an MQTT 5.0 compatible Broker. __Self__ represents a comprehensive implementation of the MQTT 5.0 protocol standard, offering full support for publishing or receiving messages with QoS 0, 1, and 2. @@ -13,12 +13,12 @@ While the MQTT protocol itself is relatively straightforward, integrating it int especially due to the challenging implementation of message retransmission after a disconnect/reconnect sequence. The aim of __Self__ is to provide a very simple asynchronous C++ interface for application developers. -The internal client's implementation manages network and MQTT protocol details. -Notably, the client does not expose connect functions (nor asynchronous connect functions); -instead, network connectivity, MQTT handshake, and message retransmission are automatically handled within the client. +The internal Client's implementation manages network and MQTT protocol details. +Notably, the Client does not expose connect functions (nor asynchronous connect functions); +instead, network connectivity, MQTT handshake, and message retransmission are automatically handled within the Client. The __Self__ interface aligns seamlessly with the __Asio__ asynchronous model. -The client's asynchronous functions are compatible with all completion tokens supported by __Asio__. +The Client's asynchronous functions are compatible with all completion tokens supported by __Asio__. [heading Features] __Self__ is a library designed with the core belief that users should focus solely on their application logic, not the network complexities. [br] @@ -26,7 +26,7 @@ The library attempts to embody this belief with a range of key features designed * [*Complete TCP, TLS/SLL, and WebSocket support] * [*User-focused simplicity]: Providing an interface that is as simple as possible without compromising functionality. -* [*Prioritized efficiency]: Utilising network and memory resources as efficiently as possible. +* [*Prioritised efficiency]: Utilising network and memory resources as efficiently as possible. * [*Minimal memory footprint]: Ensuring optimal performance in resource-constrained environments typical of IoT devices. * [*Automatic reconnect]: Automatically attempt to re-establish a connection in the event of a disconnection. * [*Fully Boost.Asio compliant]: The interfaces and implementation strategies are built upon the foundations of __Asio__. [br] @@ -44,7 +44,7 @@ In the event of a connection failure with one Broker, the Client switches to the * [*Offline buffering]: While offline, it automatically buffers all the packets to send when the connection is re-established. [heading Example] -The following example illustrates a simple scenario of configuring a Client and publishing an Application Message. +The following example illustrates a simple scenario of configuring the Client and publishing an Application Message. [!c++] #include @@ -85,12 +85,12 @@ __Self__ might be suitable for you if any of the following statements is true: * Your application uses __Asio__ and requires integrating a MQTT Client. * You require asynchronous access to an MQTT Broker. * You are developing a higher-level component that requires a connection to an MQTT Broker. -* You require a dependable and resilient MQTT Client that can automatically manage all network-related issues. +* You require a dependable and resilient MQTT Client to manage all network-related issues automatically. It may not be suitable for you if: * You solely require synchronous access to an MQTT Broker. -* The MQTT Broker you are connecting to does not support the MQTT 5 version. +* The MQTT Broker you connect to does not support the MQTT 5 version. [heading Requirements] diff --git a/doc/qbk/02_configuring_the_client.qbk b/doc/qbk/02_configuring_the_client.qbk new file mode 100644 index 0000000..2a9dade --- /dev/null +++ b/doc/qbk/02_configuring_the_client.qbk @@ -0,0 +1,84 @@ +[section:configuring_the_client Configuring the Client] +[nochunk] + +This section guides you through the steps to properly configure the __Client__ to establish a connection with your chosen MQTT Broker. + +The __Client__ does *not* expose connect functions (nor asynchronous connect functions). +Instead, it features a function that will start the Client (see [refmem mqtt_client async_run]). +All configuration of the __Client__ must be finalised before invoking [refmem mqtt_client async_run]. +Upon calling this function, the __Client__ makes its initial attempt to connect. + +[section:transport_protocol Choosing a suitable transport protocol] + +The initial step is selecting an appropriate transport protocol for your MQTT connection. + +The __MQTT__ protocol relies on an underlying transport protocol that guarantees the orderly and reliable +transmission of byte streams between the Client and Server in both directions. +Common transport protocols meeting these criteria include TCP/IP, TLS/SSL for secure transmissions, +and WebSocket over TCP/IP and TLS/SSL. + +MQTT Brokers come in various implementations and support different transport protocols. +Therefore, it is important to familiarise yourself with the protocols your chosen Broker supports. +Additionally, gather any necessary details such as certificate authority, client certificate, or private keys, +which might be required for establishing a secure connection. + +The upcoming examples will demonstrate the configuration and the publishing of a "Hello World!" application message at `QoS` 0, +using various transport protocols: + +* [link async_mqtt5.hello_world_over_tcp hello_world_over_tcp.cpp] +* [link async_mqtt5.hello_world_over_tls hello_world_over_tls.cpp] +* [link async_mqtt5.hello_world_over_websocket_tcp hello_world_over_websocket_tcp.cpp] +* [link async_mqtt5.hello_world_over_websocket_tls hello_world_over_websocket_tls.cpp] + +[endsect] [/transport_protocol] + +[section:customisation Customising your MQTT connection] + +The __Client__ offers a variety of functions for customising your MQTT connection settings. + These functionalities include: + +* *Specifying a list of Brokers:* You *must* use this function to assign a list of Brokers that the __Client__ will try to connect to (see [refmem mqtt_client brokers]). +The __Client__ allows for the specification of multiple Brokers for the connections. +It is important to ensure that *only Brokers belonging to the same cluster are included in this list*. +Listing Brokers from different clusters may lead to inconsistencies between MQTT Sessions. +* *Setting connection Credentials:* Set the authentication details (Client Identifier, User Name, Password) (see [refmem mqtt_client credentials]). +* *Configuring Keep Alive Interval:* Set the maximum allowed interval between two consecutive transmissions from the Client (see [refmem mqtt_client keep_alive]). +* *Assign a custom user-implemented authenticator:* The custom authentication will be used for __ENHANCED_AUTH__ ([refmem mqtt_client authenticator]). +* *Defining CONNECT Packet Properties:* Specify properties that will be included in the __CONNECT__ packet sent during connection initiation (see [refmem mqtt_client connect_property] and [refmem mqtt_client connect_properties]). + +It is important to note that these configurations apply to every Broker listed in [refmem mqtt_client brokers]. +To modify any configuration parameters, you must first stop the __Client__ using [refmem mqtt_client cancel] or [refmem mqtt_client async_disconnect]. +Afterwards, you can re-apply the configurations and restart the __Client__ with [refmem mqtt_client async_run]. + +[endsect] [/customisation] + +[section:establishing_a_connection Establishing a connection] + +The __Client__ initiates a connection with the first Broker from the list of Brokers assigned using [refmem mqtt_client brokers]. +A single attempt at connecting involves several steps: + +# Resolving the Broker's hostname. +# Creating a TCP connection. +# Performing the TLS/SSL Handshake /(if applicable)/. +# Performing the WebSocket Handshake /(if applicable)/. +# Performing the MQTT handshake. This involves sending the __CONNECT__ packet to the Broker, /(if applicable)/ exchanging __AUTH__ packets (see __ENHANCED_AUTH__), and continuing until the Broker sends the __CONNACK__ packet. + +The connection is successfully established once the __Client__ receives a __CONNACK__ packet with Reason Code `0x00` (`Success`). +The attempt fails if any of the following occurs: + +* Any of the steps from 1 to 5 encounters a failure. +* The __Client__ does not receive the results of the hostname resolution within `5 seconds`. +* The __Client__ is unable to complete steps 2 through 5 within `5 seconds`. + +If this connection attempt is unsuccessful, it proceeds to the next Broker in order, continuing this process until it establishes a connection or exhausts all options without success. +Upon reaching the end of the list without a successful connection, the __Client__ enters a backoff period before making another round of attempts with the entire list. +The duration of this backoff period in milliseconds is determined by the formula: + + 2^(min(retry_count, 4)) * 1000 + jitter + +In this formula, `retry_count` represents the number of complete cycles through the list without a successful connection, +and `jitter` is a randomly chosen value between `-500ms` to `500ms`, intended to prevent synchronised reconnection attempts in scenarios with multiple clients. + +[endsect] [/establishing_a_connection] + +[endsect] [/configuring_the_client] diff --git a/doc/qbk/02_examples.qbk b/doc/qbk/02_examples.qbk deleted file mode 100644 index 59cacad..0000000 --- a/doc/qbk/02_examples.qbk +++ /dev/null @@ -1,26 +0,0 @@ -[section:examples Examples] -The main class in __Self__ is __Client__, and the upcoming examples will briefly explain how to use it. - -The first examples will show two common uses of an MQTT client: as the publisher and as the receiver. - -* [link async_mqtt5.examples.publisher The publisher] -* [link async_mqtt5.examples.receiver The receiver] - -The following section will show how to use different underlying transport protocols (such as TCP, SSL and WebSocket) -to establish a connection to a MQTT Broker. - -* [link async_mqtt5.examples.network_connection Establishing a network connection with different protocols] - -The final section will showcase how to use asynchronous functions in __Client__ -with different __CompletionToken__. - -* [link async_mqtt5.examples.completion_tokens Completion tokens] - * [link async_mqtt5.examples.callbacks Async functions with callbacks] - * [link async_mqtt5.examples.cpp20_coroutines Async functions with C++20 coroutines] - * [link async_mqtt5.examples.futures Async functions with futures] - -[include examples/Tutorial.qbk] -[include examples/Network_connection.qbk] -[include examples/Completion_tokens.qbk] - -[endsect] diff --git a/doc/qbk/03_examples.qbk b/doc/qbk/03_examples.qbk new file mode 100644 index 0000000..5b3261e --- /dev/null +++ b/doc/qbk/03_examples.qbk @@ -0,0 +1,34 @@ +[section:examples Examples] +[nochunk] + +The following list contains all the examples that showcase how to use the __Client__: + +[variablelist + [ + [[link async_mqtt5.publisher publisher.cpp]] + [Shows how to use the __Client__ as a publisher.] + ] + [ + [[link async_mqtt5.receiver receiver.cpp]] + [Shows how to use the __Client__ as a receiver.] + ] + [ + [[link async_mqtt5.hello_world_over_tcp hello_world_over_tcp.cpp]] + [Publishes a "Hello World" message via TCP/IP.] + ] + [ + [[link async_mqtt5.hello_world_over_tls hello_world_over_tls.cpp]] + [Publishes a "Hello World" message via TLS/SSL.] + ] + [ + [[link async_mqtt5.hello_world_over_websocket_tcp hello_world_over_websocket_tcp.cpp]] + [Publishes a "Hello World" message via Websocket/TLS.] + ] + [ + [[link async_mqtt5.hello_world_over_websocket_tls hello_world_over_websocket_tls.cpp]] + [Publishes a "Hello World" message via Websocket/TLS.] + ] +] + +[endsect][/examples] + diff --git a/doc/qbk/examples/Completion_tokens.qbk b/doc/qbk/examples/Completion_tokens.qbk deleted file mode 100644 index 947d950..0000000 --- a/doc/qbk/examples/Completion_tokens.qbk +++ /dev/null @@ -1,111 +0,0 @@ -[section:completion_tokens Completion tokens] -The __Client__ is built upon __Asio__ and thus follows the same principles. -This section illustrates the usage of __Client__ async -functions with different __CompletionToken__. - -# [link async_mqtt5.examples.callbacks Async functions with callbacks] -# [link async_mqtt5.examples.cpp20_coroutines Async functions with C++20 coroutines] -# [link async_mqtt5.examples.futures Async functions with futures] - -[endsect] - - - -[section:callbacks Async functions with callbacks] -The following list is a reference on how to use asynchrous functions in __Client__ with callbacks. - -[import ../../../example/callbacks.cpp] - -[h4 Publish] -[publish_callback] - -[h4 Subscribe] -[subscribe_callback] - -[h4 Receive] -[receive_callback] - -[h4 Unsubscribe] -[unsubscribe_callback] - -[h4 Disconnect] -[disconnect_callback] - -[endsect] - -[section:cpp20_coroutines Async functions with C++20 coroutines] -This example demonstrates how to use __Client__ asynchrous functions with C++20 coroutines -using __USE_AWAITABLE__. - -[import ../../../example/cpp20_coroutines.cpp] - -[h2 use_awaitable] -In this section, each asynchronous function is invoked with __USE_AWAITABLE__ completion token. -When using this completion token, co_await will throw exceptions instead of returning an error code. -If you do not wish to throw exceptions, refer to the following use_nothrow_awaitable section. - -[h4 Publish] -[publish_coro] - -[h4 Subscribe] -[subscribe_coro] - -[h4 Receive] -[receive_coro] - -[h4 Unsubscribe] -[unsubscribe_coro] - -[h4 Disconnect] -[disconnect_coro] - -[h2 use_nothrow_awaitable] -The following examples will use a modified completion token. -Using this completion token instead of __USE_AWAITABLE__ will prevent co_await from throwing exceptions. -Instead, co_await will return the error code along with other values specified in the handler signature. - -[no_throw_awaitable] - -[h4 Publish] -[publish_coro_nothrow] - -[h4 Subscribe] -[subscribe_coro_nothrow] - -[h4 Receive] -[receive_coro_nothrow] - -[h4 Unsubscribe] -[unsubscribe_coro_nothrow] - -[h4 Disconnect] -[disconnect_coro_nothrow] - -[endsect] - - - -[section:futures Async functions with futures] -The following list is a reference on how to use the mqtt_client with __USE_FUTURE__ as the completion token. - -Each get() call on std::future will block the current thread and wait until the future has a valid result. -That is why it is essential to ensure that the execution context is running in more than one thread. - -[import ../../../example/futures.cpp] - -[h4 Publish] -[publish_future] - -[h4 Subscribe] -[subscribe_future] - -[h4 Receive] -[receive_future] - -[h4 Unsubscribe] -[unsubscribe_future] - -[h4 Disconnect] -[disconnect_future] - -[endsect] diff --git a/doc/qbk/examples/Examples.qbk b/doc/qbk/examples/Examples.qbk new file mode 100644 index 0000000..478379e --- /dev/null +++ b/doc/qbk/examples/Examples.qbk @@ -0,0 +1,43 @@ +[block''''''] + +[section:hello_world_over_tcp Hello World over TCP/IP] +This example illustrates the process of setting up the Client to connect to the Broker via TCP/IP and publish a "Hello World!" message. +[import ../../../example/hello_world_over_tcp.cpp] +[hello_world_over_tcp] +[endsect] [/hello_world_over_tcp] + +[section:hello_world_over_tls Hello World over TLS/SSL] +This example illustrates the process of setting up the Client to connect to the Broker via TLS/SSL and publish a "Hello World!" message. +[import ../../../example/hello_world_over_tls.cpp] +[hello_world_over_tls] +[endsect] [/hello_world_over_tls] + +[section:hello_world_over_websocket_tcp Hello World over Websocket/TCP] +This example illustrates the process of setting up the Client to connect to the Broker via Websocket/TCP and publish a "Hello World!" message. +[import ../../../example/hello_world_over_websocket_tcp.cpp] +[hello_world_over_websocket_tcp] +[endsect] [/hello_world_over_websocket_tcp] + +[section:hello_world_over_websocket_tls Hello World over Websocket/TLS] +This example illustrates the process of setting up the Client to connect to the Broker via Websocket/TLS and publish a "Hello World!" message. +[import ../../../example/hello_world_over_websocket_tls.cpp] +[hello_world_over_websocket_tls] +[endsect] [/hello_world_over_websocket_tls] + +[section:publisher The publisher] +This example will show how to use __Client__ as a publisher. +The __Client__ will use TCP to connect to the Broker and __USE_AWAITABLE__ as the completion token. + +[import ../../../example/publisher.cpp] +[publisher] +[endsect] + +[section:receiver The receiver] +This example will show how to use __Client__ as a receiver. +The __Client__ will use TCP to connect to the Broker and __USE_AWAITABLE__ as the completion token. + +[import ../../../example/receiver.cpp] +[receiver] +[endsect] + +[block''''''] diff --git a/doc/qbk/examples/Network_connection.qbk b/doc/qbk/examples/Network_connection.qbk deleted file mode 100644 index 0090b5c..0000000 --- a/doc/qbk/examples/Network_connection.qbk +++ /dev/null @@ -1,36 +0,0 @@ -[section:network_connection Establishing a network connection with different protocols ] -The __MQTT__ protocol requires the underlying transport protocol that ensures -orderly, lossless transmission of byte streams between the Client and Server in both directions. - -The following examples demonstrate how to establish a network connection using suitable -transport protocols such as TCP/IP, TLS/SSL, and WebSocket. - -[import ../../../example/network_connection.cpp] - -[h3 TCP/IP connection] -To create a TCP/IP connection with a Broker, initialise __Client__ with __TCP_SOCKET__ as the __StreamType__. -[tcp] - -[h3 TLS/SSL connection] -To establish a secure and encrypted connection using the TLS/SSL protocol, supply a context object that meets the __TlsContext__ requirements. -Additionally, initialise __Client__ with an underlying stream that implements TLS/SSL protocol as the __StreamType__. - -This example will demonstrate how to set up an SSL connection using __SSL_CONTEXT__ and __SSL_STREAM__. -To use SSL support in __Asio__, __OPENSSL__ is required. -[tls] - -[h3 WebSocket connection] -The WebSocket connection can be established over an unencrypted TCP/IP connection or an encrypted TLS/SSL connection. - -The following example will showcase setting up WebSocket over TCP/IP and WebSocket over TLS/SLL connection using -__WEBSOCKET_STREAM__. - -[h4 WebSocket over TCP/IP] -[websocket_tcp] - -[h4 WebSocket over TLS/SSL] -[websocket_tls] - -Once the __Client__ has been initialised with a suitable __StreamType__, it is prepared for configuration and utilisation. - -[endsect] diff --git a/doc/qbk/examples/Tutorial.qbk b/doc/qbk/examples/Tutorial.qbk deleted file mode 100644 index 9f81f0b..0000000 --- a/doc/qbk/examples/Tutorial.qbk +++ /dev/null @@ -1,23 +0,0 @@ -[section:publisher The publisher] -This example will show how to use __Client__ as a publisher. -The __Client__ will use TCP to connect to the Broker, and __USE_AWAITABLE__ as the completion token. - -To modify the connection type, refer to [link async_mqtt5.examples.network_connection Establishing a network connection with different protocols]. - -To use different completion token, refer to [link async_mqtt5.examples.completion_tokens Completion tokens]. - -[import ../../../example/publisher.cpp] -[publisher] -[endsect] - -[section:receiver The receiver] -This example will show how to use __Client__ as a receiver. -The __Client__ will use TCP to connect to the Broker, and __USE_AWAITABLE__ as the completion token. - -To modify the connection type, refer to [link async_mqtt5.examples.network_connection Establishing a network connection with different protocols]. - -To use different completion token, refer to [link async_mqtt5.examples.completion_tokens Completion tokens]. - -[import ../../../example/receiver.cpp] -[receiver] -[endsect] \ No newline at end of file diff --git a/doc/qbk/reference/quickref.xml b/doc/qbk/reference/quickref.xml index 1423108..99a9171 100644 --- a/doc/qbk/reference/quickref.xml +++ b/doc/qbk/reference/quickref.xml @@ -31,7 +31,6 @@ Enumerations auth_step_e - connection::error client::error disconnect_rc_e qos_e diff --git a/doc/reference.xsl b/doc/reference.xsl index 90a6375..4558556 100644 --- a/doc/reference.xsl +++ b/doc/reference.xsl @@ -20,10 +20,6 @@ --> - -[section:ref Reference] -[xinclude quickref.xml] - [include Error_handling.qbk] [include concepts/ExecutionContext.qbk] [include concepts/StreamType.qbk] @@ -70,10 +66,6 @@ - - [endsect] - - @@ -1707,7 +1699,6 @@ [endsect] - [endsect] diff --git a/example/hello_world_over_tcp.cpp b/example/hello_world_over_tcp.cpp new file mode 100644 index 0000000..6e9ef25 --- /dev/null +++ b/example/hello_world_over_tcp.cpp @@ -0,0 +1,31 @@ +//[hello_world_over_tcp +#include + +#include +#include +#include + +#include + +int main() { + boost::asio::io_context ioc; + + // Construct the Client with ``__TCP_SOCKET__`` as the underlying stream. + async_mqtt5::mqtt_client client(ioc); + + // 1883 is the default TCP MQTT port. + client.brokers("", 1883) + .async_run(boost::asio::detached); + + 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); + } + ); + + ioc.run(); +} +//] diff --git a/example/hello_world_over_tls.cpp b/example/hello_world_over_tls.cpp new file mode 100644 index 0000000..6ffb6ec --- /dev/null +++ b/example/hello_world_over_tls.cpp @@ -0,0 +1,85 @@ +//[hello_world_over_tls +#include + +#include +#include +#include +#include + +#include + +// External customization point. +namespace async_mqtt5 { + +template +struct tls_handshake_type> { + static constexpr auto client = boost::asio::ssl::stream_base::client; + static constexpr auto server = boost::asio::ssl::stream_base::server; +}; + +// This client uses this function to indicate which hostname it is +// attempting to connect to at the start of the handshaking process. +template +void assign_tls_sni( + const authority_path& ap, + boost::asio::ssl::context& ctx, + boost::asio::ssl::stream& stream +) { + SSL_set_tlsext_host_name(stream.native_handle(), ap.host.c_str()); +} + +} // end namespace async_mqtt5 + +// The certificate file in the PEM format. +constexpr char ca_cert[] = +"-----BEGIN CERTIFICATE-----\n" +"...........................\n" +"-----END CERTIFICATE-----\n" +; + +int main() { + boost::asio::io_context ioc; + + // Context satisfying ``__TlsContext__`` requirements that the underlying SSL stream will use. + // The purpose of the context is to allow us to set up TLS/SSL-related options. + // See ``__SSL__`` for more information and options. + boost::asio::ssl::context context(boost::asio::ssl::context::tls_client); + + async_mqtt5::error_code ec; + + // Add the trusted certificate authority for performing verification. + context.add_certificate_authority(boost::asio::buffer(ca_cert), ec); + if (ec) + std::cout << "Failed to add certificate authority!" << std::endl; + ec.clear(); + + // Set peer verification mode used by the context. + // This will verify that the server's certificate is valid and signed by a trusted certificate authority. + context.set_verify_mode(boost::asio::ssl::verify_peer, ec); + if (ec) + std::cout << "Failed to set peer verification mode!" << std::endl; + ec.clear(); + + // Construct the Client with ``__SSL_STREAM__`` as the underlying stream + // with ``__SSL_CONTEXT__`` as the ``__TlsContext__`` type. + async_mqtt5::mqtt_client< + boost::asio::ssl::stream, + boost::asio::ssl::context + > client(ioc, std::move(context)); + + // 8883 is the default TLS MQTT port. + client.brokers("", 8883) + .async_run(boost::asio::detached); + + 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); + } + ); + + ioc.run(); +} +//] diff --git a/example/hello_world_over_websocket_tcp.cpp b/example/hello_world_over_websocket_tcp.cpp new file mode 100644 index 0000000..2b35305 --- /dev/null +++ b/example/hello_world_over_websocket_tcp.cpp @@ -0,0 +1,35 @@ +//[hello_world_over_websocket_tcp +#include + +#include +#include +#include + +#include + +#include + +int main() { + boost::asio::io_context ioc; + + // Construct the Client with ``[beastreflink boost__beast__websocket__stream websocket::stream<__TCP_SOCKET__>]`` as the underlying stream. + async_mqtt5::mqtt_client< + boost::beast::websocket::stream + > client(ioc); + + // 8083 is the default Webscoket/TCP MQTT port. + client.brokers("", 8083) + .async_run(boost::asio::detached); + + 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); + } + ); + + ioc.run(); +} +//] diff --git a/example/hello_world_over_websocket_tls.cpp b/example/hello_world_over_websocket_tls.cpp new file mode 100644 index 0000000..b6f2037 --- /dev/null +++ b/example/hello_world_over_websocket_tls.cpp @@ -0,0 +1,107 @@ +//[hello_world_over_websocket_tls +#include + +#include +#include +#include +#include + +#include + +#include + +namespace boost::beast::websocket { + +// ``[beastreflink boost__beast__websocket__async_teardown boost::beast::websocket::async_teardown]`` is a free function +// designed to initiate the asynchronous teardown of a connection. +// The specific behaviour of this function is based on the NextLayer type (Socket type) used to create the ``__WEBSOCKET_STREAM__``. +// ``__Beast__`` library includes an implementation of this function for ``__TCP_SOCKET__``. +// However, the callers are responsible for providing a suitable overload of this function for any other type, +// such as ``__SSL_STREAM__`` as shown in this example. +template +void async_teardown( + boost::beast::role_type role, + asio::ssl::stream& stream, + TeardownHandler&& handler +) { + return stream.async_shutdown(std::forward(handler)); +} + +} // end namespace boost::beast::websocket + + +// External customization point. +namespace async_mqtt5 { + +template +struct tls_handshake_type> { + static constexpr auto client = boost::asio::ssl::stream_base::client; + static constexpr auto server = boost::asio::ssl::stream_base::server; +}; + +// This client uses this function to indicate which hostname it is +// attempting to connect to at the start of the handshaking process. +template +void assign_tls_sni( + const authority_path& ap, + boost::asio::ssl::context& ctx, + boost::asio::ssl::stream& stream +) { + SSL_set_tlsext_host_name(stream.native_handle(), ap.host.c_str()); +} + +} // end namespace async_mqtt5 + +// The certificate file in the PEM format. +constexpr char ca_cert[] = +"-----BEGIN CERTIFICATE-----\n" +"...........................\n" +"-----END CERTIFICATE-----\n" +; + +int main() { + boost::asio::io_context ioc; + + // Context satisfying ``__TlsContext__`` requirements that the underlying SSL stream will use. + // The purpose of the context is to allow us to set up TLS/SSL-related options. + // See ``__SSL__`` for more information and options. + boost::asio::ssl::context context(boost::asio::ssl::context::tls_client); + + async_mqtt5::error_code ec; + + // Add the trusted certificate authority for performing verification. + context.add_certificate_authority(boost::asio::buffer(ca_cert), ec); + if (ec) + std::cout << "Failed to add certificate authority!" << std::endl; + ec.clear(); + + // Set peer verification mode used by the context. + // This will verify that the server's certificate is valid and signed by a trusted certificate authority. + context.set_verify_mode(boost::asio::ssl::verify_peer, ec); + if (ec) + std::cout << "Failed to set peer verification mode!" << std::endl; + ec.clear(); + + // Construct the Client with ``[beastreflink boost__beast__websocket__stream websocket::stream<__SSL_STREAM__>]`` + // as the underlying stream with ``__SSL_CONTEXT__`` as the ``__TlsContext__`` type. + async_mqtt5::mqtt_client< + boost::beast::websocket::stream>, + boost::asio::ssl::context + > client(ioc, std::move(context)); + + // 8884 is the default Websocket/TLS MQTT port. + client.brokers("", 8884) + .async_run(boost::asio::detached); + + 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); + } + ); + + ioc.run(); +} +//] \ No newline at end of file diff --git a/example/network_connection.cpp b/example/network_connection.cpp deleted file mode 100644 index e5f91d1..0000000 --- a/example/network_connection.cpp +++ /dev/null @@ -1,176 +0,0 @@ -//[network_connectiong - - -//[tcp -#include - -#include - -#include - -void tcp_setup() { - boost::asio::io_context ioc; - - // Use ``__TCP_SOCKET__`` as the underlying stream. - async_mqtt5::mqtt_client client(ioc); -} - -//] - - -//[tls -#include -#include - -#include - -#include - - -// External customization point. -namespace async_mqtt5 { - -template -struct tls_handshake_type> { - static constexpr auto client = asio::ssl::stream_base::client; - static constexpr auto server = asio::ssl::stream_base::server; -}; - -// This client uses this funcction to indicate which hostname it is -// attempting to connect to at the start of the handshaking process. -template -void assign_tls_sni( - const authority_path& ap, - boost::asio::ssl::context& ctx, - boost::asio::ssl::stream& stream -) { - SSL_set_tlsext_host_name(stream.native_handle(), ap.host.c_str()); -} - -} // end namespace async_mqtt5 - -// The certificate file in PEM format. -constexpr char certificate[] = -"-----BEGIN CERTIFICATE-----\n" -"...........................\n" -"-----END CERTIFICATE-----\n" -; - -void ssl_setup() { - boost::asio::io_context ioc; - - // Context satisfying ``__TlsContext__`` requirements that the underlying SSL stream will use. - // The purpose of the context is to allow us to set up TLS/SSL-related options. - // See ``__SSL__`` for more information and options. - boost::asio::ssl::context context(boost::asio::ssl::context::tls_client); - - async_mqtt5::error_code ec; - - // Add the trusted certificate authority for performing verification. - context.add_certificate_authority(boost::asio::buffer(certificate), ec); - - // Set peer verification mode used by the context. - // This will verify that the server's certificate is valid and signed by a trusted certificate authority. - context.set_verify_mode(boost::asio::ssl::verify_peer, ec); - - // Use ``__SSL_STREAM__`` as the ``__StreamType__`` with ``__SSL_CONTEXT__`` as the ``__TlsContext__``. - async_mqtt5::mqtt_client< - boost::asio::ssl::stream, - boost::asio::ssl::context - > client(ioc, std::move(context)); -} -//] - -//[websocket_tcp -#include - -#include - -#include - -#include - -void websocket_tcp_setup() { - boost::asio::io_context ioc; - - // Use ``[beastreflink boost__beast__websocket__stream websocket::stream<__TCP_SOCKET__>]`` as the underlying stream. - async_mqtt5::mqtt_client< - boost::beast::websocket::stream - > client(ioc); -} - -//] - -//[websocket_tls -#include -#include - -#include - -#include - -#include - -constexpr char ca[] = -"-----BEGIN CERTIFICATE-----\n" -"...........................\n" -"-----END CERTIFICATE-----\n" -; - -namespace boost::beast::websocket { - -// ``[beastreflink boost__beast__websocket__async_teardown boost::beast::websocket::async_teardown]`` is a free function -// designed to initiate the asynchronous teardown of a connection. -// The specific behaviour of this function is based on the NextLayer type (Socket type) used to create the ``__WEBSOCKET_STREAM__``. -// ``__Beast__`` library includes an implementation of this function for ``__TCP_SOCKET__``. -// However, the callers are responsible for providing a suitable overload of this function for any other type, -// such as ``__SSL_STREAM__`` as shown in this example. -template -void async_teardown( - boost::beast::role_type role, - asio::ssl::stream& stream, - TeardownHandler&& handler -) { - return stream.async_shutdown(std::forward(handler)); -} - -} // end namespace boost::beast::websocket - -namespace async_mqtt5 { - -template -struct tls_handshake_type> { - static constexpr auto client = asio::ssl::stream_base::client; - static constexpr auto server = asio::ssl::stream_base::server; -}; - -template -void assign_tls_sni( - const authority_path& ap, - asio::ssl::context& ctx, - asio::ssl::stream& stream -) { - ssl_set_tlsext_host_name(stream.native_handle(), ap.host.c_str()); -} - -} // end namespace async_mqtt5 - -void websocket_tls_setup() { - boost::asio::io_context ioc; - - boost::asio::ssl::context context(boost::asio::ssl::context::tls_client); - - async_mqtt5::error_code ec; - context.add_certificate_authority(boost::asio::buffer(ca), ec); - context.set_verify_mode(boost::asio::ssl::verify_peer, ec); - - async_mqtt5::mqtt_client< - boost::beast::websocket::stream>, - boost::asio::ssl::context - > client(ioc, std::move(context)); -} - -//] - - -//]