1
0
mirror of https://github.com/eclipse/paho.mqtt.cpp.git synced 2025-05-09 11:21:24 +08:00

- Added properties iterator.

- Added property typeid, type name, and cleaned up constructor.
- Exception checks if return code is reason code.
- Added 'server_property_v5' example app.
This commit is contained in:
fpagliughi 2024-06-25 19:52:21 -04:00
parent e3d6ad0710
commit 381caad141
7 changed files with 329 additions and 42 deletions

View File

@ -35,18 +35,19 @@ set(EXECUTABLES
async_subscribe
async_consume
async_consume_v5
data_publish
mqttpp_chat
multithr_pub_sub
pub_speed_test
rpc_math_cli
rpc_math_srvr
server_props_v5
sync_publish
sync_consume
sync_consume_v5
sync_reconnect
data_publish
rpc_math_cli
rpc_math_srvr
mqttpp_chat
topic_publish
multithr_pub_sub
ws_publish
pub_speed_test
)
# These will only be built if SSL selected

View File

@ -130,7 +130,8 @@ int main(int argc, char* argv[])
auto top = mqtt::topic(cli, "data/time", QOS);
cout << "Publishing data..." << endl;
while (timestamp() % DELTA_MS != 0);
while (timestamp() % DELTA_MS != 0)
;
uint64_t t = timestamp(), tlast = t, tstart = t;

View File

@ -205,7 +205,8 @@ int main(int argc, char* argv[])
// Just block till user tells us to quit.
while (std::tolower(std::cin.get()) != 'q');
while (std::tolower(std::cin.get()) != 'q')
;
// Disconnect

View File

@ -0,0 +1,117 @@
// server_props_v5.cpp
//
// This is a Paho MQTT C++ client, sample application.
//
// This application is an MQTT client using the C++ asynchronous interface
// which shows how to check the server cofiguration for an MQTT v5
// connection.
//
// With an MQTT v5 connection, the server specify can some features that it
// doesn't supports, or limits in some way. It does this by adding v5
// properties to the CONNACK packet it sends back to the client in a connect
// transaction. The C++ application can retrieve these from the connect
// token via the `connect_response` object.
//
// It also shows short-lived persistent sessions. The client asks the server
// to just keep the session for 10sec. If you re-run the application in less
// than 10sec, it should report that the session exists. Any longer, and the
// session will be gone.
//
// Note that 10sec is probably *way too short* a time for real-world
// applications. This is just for demonstrating/testing the session expiry
// interval.
//
// The sample demonstrates:
// - Connecting to an MQTT v5 server/broker.
// - Specifying a short-lived (10sec) persistent session.
// - Retrieving the v5 properties from the connect response (i.e. CONNACK
// packet)
// - Iterating through v5 properties.
// - Displaying server properties to the user.
//
/*******************************************************************************
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
* and Eclipse Distribution License v1.0 which accompany this distribution.
*
* The Eclipse Public License is available at
* http://www.eclipse.org/legal/epl-v20.html
* and the Eclipse Distribution License is available at
* http://www.eclipse.org/org/documents/edl-v10.php.
*
* Contributors:
* Frank Pagliughi - initial implementation and documentation
*******************************************************************************/
#include <cctype>
#include <chrono>
#include <cstdlib>
#include <cstring>
#include <iostream>
#include <string>
#include <thread>
#include "mqtt/async_client.h"
using namespace std;
const string DFLT_SERVER_URI{"mqtt://localhost:1883"};
const string CLIENT_ID{"server_props_v5"};
/////////////////////////////////////////////////////////////////////////////
int main(int argc, char* argv[])
{
auto serverURI = (argc > 1) ? string{argv[1]} : DFLT_SERVER_URI;
mqtt::async_client cli(serverURI, CLIENT_ID);
auto connOpts = mqtt::connect_options_builder::v5()
.clean_start(false)
.properties({{mqtt::property::SESSION_EXPIRY_INTERVAL, 10}})
.finalize();
try {
// Connect to the server
cout << "Connecting to the MQTT server at '" << serverURI << "'..." << flush;
auto tok = cli.connect(connOpts);
// Getting the connect response will block waiting for the
// connection to complete.
auto rsp = tok->get_connect_response();
cout << "OK" << endl;
// Make sure we were granted a v5 connection.
if (rsp.get_mqtt_version() < MQTTVERSION_5) {
cout << "Did not get an MQTT v5 connection." << endl;
exit(1);
}
// Does the server have a session for us?
cout << "\nThe session is " << (rsp.is_session_present() ? "" : "not ")
<< "present on the server." << endl;
// Show the v5 properties from the CONNACK, if any
cout << "\nConnection Properties:" << endl;
if (rsp.get_properties().empty()) {
cout << " <none>" << endl;
}
else {
for (const auto& prop : rsp.get_properties()) {
cout << " " << prop << endl;
}
}
// OK, we're done.
cli.disconnect()->wait();
}
catch (const mqtt::exception& exc) {
cerr << "\n " << exc << endl;
return 1;
}
return 0;
}

View File

@ -6,7 +6,7 @@
/////////////////////////////////////////////////////////////////////////////
/*******************************************************************************
* Copyright (c) 2013-2019 Frank Pagliughi <fpagliughi@mindspring.com>
* Copyright (c) 2013-2024 Frank Pagliughi <fpagliughi@mindspring.com>
*
* All rights reserved. This program and the accompanying materials
* are made available under the terms of the Eclipse Public License v2.0
@ -54,6 +54,13 @@ protected:
/** The error message from the C library */
string msg_;
/** See if the return code is actually a reason code error value */
static ReasonCode reason_code(int rc, ReasonCode reasonCode) {
if (reasonCode == ReasonCode::SUCCESS && rc >= ReasonCode::UNSPECIFIED_ERROR)
reasonCode = ReasonCode(rc);
return reasonCode;
}
public:
/**
* Creates an MQTT exception.
@ -72,11 +79,7 @@ public:
* @param rc The error return code from the C library.
* @param msg The text message for the error.
*/
exception(int rc, const string& msg)
: std::runtime_error(printable_error(rc, ReasonCode::SUCCESS, msg)),
rc_(rc),
reasonCode_(ReasonCode::SUCCESS),
msg_(msg) {}
exception(int rc, const string& msg) : exception(rc, ReasonCode::SUCCESS, msg) {}
/**
* Creates an MQTT exception.
* @param rc The error return code from the C library.
@ -85,9 +88,9 @@ public:
*/
exception(int rc, ReasonCode reasonCode, const string& msg)
: std::runtime_error(printable_error(rc, reasonCode, msg)),
rc_(rc),
reasonCode_(reasonCode),
msg_(msg) {}
rc_{rc},
reasonCode_{reason_code(rc, reasonCode)},
msg_{msg} {}
/**
* Gets an error message from an error code.
* @param rc The error code from the C lib
@ -116,13 +119,15 @@ public:
* explanation message.
*/
static string printable_error(
int rc, int reasonCode = ReasonCode::SUCCESS, const string& msg = string()
int rc, ReasonCode reasonCode = ReasonCode::SUCCESS, const string& msg = string()
) {
reasonCode = reason_code(rc, reasonCode);
string s = "MQTT error [" + std::to_string(rc) + "]";
if (!msg.empty())
s += string(": ") + msg;
if (reasonCode != ReasonCode::SUCCESS)
s += string(". Reason: ") + reason_code_str(reasonCode);
s += string(". ") + reason_code_str(reasonCode);
return s;
}
/**

View File

@ -30,7 +30,10 @@ extern "C" {
#include <initializer_list>
#include <iostream>
#include <map>
#include <string_view>
#include <tuple>
#include <typeinfo>
#include "mqtt/buffer_ref.h"
#include "mqtt/exception.h"
@ -56,6 +59,9 @@ class property
// For string properties, this allocates memory and copied the string(s)
void copy(const MQTTProperty& other);
friend class properties;
property() {}
public:
/**
* The integer codes for the different v5 properties.
@ -90,6 +96,9 @@ public:
SHARED_SUBSCRIPTION_AVAILABLE = 42
};
/** The names of the different types of properties */
PAHO_MQTTPP_EXPORT static const std::map<code, std::string_view> TYPE_NAME;
/**
* Create a numeric property.
* This can be a byte, or 2-byte, 4-byte, or variable byte integer.
@ -121,18 +130,20 @@ public:
* Creates a property from a C struct.
* @param cprop A C struct for a property list.
*/
explicit property(const MQTTProperty& cprop);
explicit property(const MQTTProperty& cprop) { copy(cprop); }
/**
* Moves a C struct into this property list.
* This takes ownership of any memory that the C struct is holding.
* @param cprop A C struct for a property list.
*/
explicit property(MQTTProperty&& cprop);
explicit property(MQTTProperty&& cprop) : prop_(cprop) {
memset(&cprop, 0, sizeof(MQTTProperty));
}
/**
* Copy constructor
* @param other The other property to copy into this one.
*/
property(const property& other);
property(const property& other) { copy(other.prop_); }
/**
* Move constructor.
* @param other The other property that is moved into this one.
@ -169,9 +180,16 @@ public:
* Gets a printable name for the property type.
* @return A printable name for the property type.
*/
const char* type_name() const { return ::MQTTPropertyName(prop_.identifier); }
std::string_view type_name() const;
/**
* Gets the typeid for the value contained in the property.
* @return The typeid for the value contained in the property.
*/
const std::type_info& value_type_id();
};
std::ostream& operator<<(std::ostream& os, const property& prop);
/**
* Extracts the value from the property as the specified type.
* @return The value from the property as the specified type.
@ -271,10 +289,10 @@ inline string_pair get<string_pair>(const property& prop) {
class properties
{
/** The default C struct */
PAHO_MQTTPP_EXPORT static const MQTTProperties DFLT_C_STRUCT;
static constexpr MQTTProperties DFLT_C_STRUCT MQTTProperties_initializer;
/** The underlying C properties struct */
MQTTProperties props_;
MQTTProperties props_{DFLT_C_STRUCT};
template <typename T>
friend T get(const properties& props, property::code propid, size_t idx);
@ -283,11 +301,58 @@ class properties
friend T get(const properties& props, property::code propid);
public:
/** A const iterator for the properties list */
class const_iterator
{
const MQTTProperty* curr_;
mutable property prop_;
friend properties;
const_iterator(const MQTTProperty* curr) : curr_{curr} {}
public:
/**
* Gets a reference to the current value.
* @return A reference to the current value.
*/
const property& operator*() const {
prop_ = property{*curr_};
return prop_;
}
/**
* Postfix increment operator.
* @return An iterator pointing to the previous matching item.
*/
const_iterator operator++(int) noexcept {
auto tmp = *this;
curr_++;
return tmp;
}
/**
* Prefix increment operator.
* @return An iterator pointing to the next matching item.
*/
const_iterator& operator++() noexcept {
++curr_;
return *this;
}
/**
* Compares two iterators to see if they don't refer to the same
* node.
*
* @param other The other iterator to compare against this one.
* @return @em true if they don't match, @em false if they do
*/
bool operator!=(const const_iterator& other) const noexcept {
return curr_ != other.curr_;
}
};
/**
* Default constructor.
* Creates an empty properties list.
*/
properties();
properties() {}
/**
* Copy constructor.
* @param other The property list to copy.
@ -342,6 +407,26 @@ public:
* @return The number of property items in the list.
*/
size_t size() const { return size_t(props_.count); }
/**
* Gets a const iterator to the full collection of properties.
* @return A const iterator to the full collection of properties.
*/
const_iterator begin() const { return const_iterator{props_.array}; }
/**
* Gets a const iterator to the full collection of properties.
* @return A const iterator to the full collection of properties.
*/
const_iterator cbegin() const { return begin(); }
/**
* Gets a const iterator to the end of the collection of properties.
* @return A const iterator to the end of collection of properties.
*/
const_iterator end() const { return const_iterator{props_.array + size()}; }
/**
* Gets a const iterator to the end of the collection of properties.
* @return A const iterator to the end of collection of properties.
*/
const_iterator cend() const { return end(); }
/**
* Adds a property to the list.
* @param prop The property to add to the list.

View File

@ -20,6 +20,36 @@
namespace mqtt {
PAHO_MQTTPP_EXPORT const std::map<property::code, std::string_view> property::TYPE_NAME{
{PAYLOAD_FORMAT_INDICATOR, "PayloadFormatIndicator"},
{MESSAGE_EXPIRY_INTERVAL, "MessageExpiryInterval"},
{CONTENT_TYPE, "ContentType"},
{RESPONSE_TOPIC, "ResponseTopic"},
{CORRELATION_DATA, "CorrelationData"},
{SUBSCRIPTION_IDENTIFIER, "SubscriptionIdentifier"},
{SESSION_EXPIRY_INTERVAL, "SessionExpiryInterval"},
{ASSIGNED_CLIENT_IDENTIFIER, "AssignedClientIdentifer"},
{SERVER_KEEP_ALIVE, "ServerKeepAlive"},
{AUTHENTICATION_METHOD, "AuthenticationMethod"},
{AUTHENTICATION_DATA, "AuthenticationData"},
{REQUEST_PROBLEM_INFORMATION, "RequestProblemInformation"},
{WILL_DELAY_INTERVAL, "WillDelayInterval"},
{REQUEST_RESPONSE_INFORMATION, "RequestResponseInformation"},
{RESPONSE_INFORMATION, "ResponseInformation"},
{SERVER_REFERENCE, "ServerReference"},
{REASON_STRING, "ReasonString"},
{RECEIVE_MAXIMUM, "ReceiveMaximum"},
{TOPIC_ALIAS_MAXIMUM, "TopicAliasMaximum"},
{TOPIC_ALIAS, "TopicAlias"},
{MAXIMUM_QOS, "MaximumQos"},
{RETAIN_AVAILABLE, "RetainAvailable"},
{USER_PROPERTY, "UserProperty"},
{MAXIMUM_PACKET_SIZE, "MaximumPacketSize"},
{WILDCARD_SUBSCRIPTION_AVAILABLE, "WildcardSubscriptionAvailable"},
{SUBSCRIPTION_IDENTIFIERS_AVAILABLE, "SubscriptionIdentifiersAvailable"},
{SHARED_SUBSCRIPTION_AVAILABLE, "SharedSubscriptionAvailable"}
};
/////////////////////////////////////////////////////////////////////////////
property::property(code c, int32_t val)
@ -68,15 +98,6 @@ property::property(code c, string_ref name, string_ref val)
std::memcpy(prop_.value.value.data, val.data(), n);
}
property::property(const MQTTProperty& cprop) { copy(cprop); }
property::property(MQTTProperty&& cprop) : prop_(cprop)
{
memset(&cprop, 0, sizeof(MQTTProperty));
}
property::property(const property& other) { copy(other.prop_); }
property::property(property&& other)
{
std::memcpy(&prop_, &other.prop_, sizeof(MQTTProperty));
@ -144,16 +165,72 @@ property& property::operator=(property&& rhs)
return *this;
}
std::string_view property::type_name() const
{
if (auto p = TYPE_NAME.find(code(prop_.identifier)); p != TYPE_NAME.end()) {
return p->second;
}
return std::string_view("Unknown");
}
const std::type_info& property::value_type_id()
{
switch (::MQTTProperty_getType(prop_.identifier)) {
case MQTTPROPERTY_TYPE_BYTE:
return typeid(uint8_t);
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
return typeid(uint16_t);
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
return typeid(uint32_t);
case MQTTPROPERTY_TYPE_BINARY_DATA:
return typeid(binary);
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
return typeid(string);
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
return typeid(string_pair);
}
return typeid(int);
}
std::ostream& operator<<(std::ostream& os, const property& prop)
{
os << prop.type_name() << ": ";
switch (::MQTTProperty_getType(MQTTPropertyCodes(prop.type()))) {
case MQTTPROPERTY_TYPE_BYTE:
case MQTTPROPERTY_TYPE_TWO_BYTE_INTEGER:
case MQTTPROPERTY_TYPE_FOUR_BYTE_INTEGER:
case MQTTPROPERTY_TYPE_VARIABLE_BYTE_INTEGER:
os << get<int>(prop);
break;
case MQTTPROPERTY_TYPE_BINARY_DATA: {
auto bin = get<binary>(prop);
for (const char& by : bin) os << std::hex << unsigned(by);
os << std::dec;
} break;
case MQTTPROPERTY_TYPE_UTF_8_ENCODED_STRING:
os << get<string>(prop);
break;
case MQTTPROPERTY_TYPE_UTF_8_STRING_PAIR:
auto sp = get<string_pair>(prop);
os << '(' << std::get<0>(sp) << ',' << std::get<1>(sp) << ')';
break;
}
return os;
}
/////////////////////////////////////////////////////////////////////////////
PAHO_MQTTPP_EXPORT const MQTTProperties properties::DFLT_C_STRUCT =
MQTTProperties_initializer;
properties::properties() : props_{DFLT_C_STRUCT} {}
properties::properties(std::initializer_list<property> props) : props_{DFLT_C_STRUCT}
properties::properties(std::initializer_list<property> props)
{
for (const auto& prop : props) ::MQTTProperties_add(&props_, &prop.c_struct());
for (const auto& prop : props) {
::MQTTProperties_add(&props_, &prop.c_struct());
}
}
properties& properties::operator=(const properties& rhs)