Descubrimiento rápido de multidifusión CoAP
Uso del cliente CoAP para un descubrimiento de recursos multicast con una interfaz de usuario Qt Quick.

El ejemplo Quick CoAP Multicast Disco very muestra cómo registrar QCoapClient como tipo QML y utilizarlo en una aplicación Qt Quick para el descubrimiento de recursos de multidifusión CoAP.
Nota: Qt CoAP no proporciona una API QML en su versión actual. Sin embargo, puede hacer que las clases C++ del módulo estén disponibles para QML como se muestra en este ejemplo.
Ejecución del ejemplo
Para ejecutar el ejemplo desde Qt Creatorabra el modo Welcome y seleccione el ejemplo de Examples. Para más información, consulte Qt Creator: Tutorial: Construir y ejecutar.
Configuración de un servidor CoAP
Para ejecutar la aplicación de ejemplo, primero necesitas configurar e iniciar al menos un servidor CoAP que soporte el descubrimiento de recursos multicast. Dispone de las siguientes opciones:
- Construir y ejecutar manualmente servidores CoAP utilizando libcoap, Californium, o cualquier otra implementación de servidor CoAP, que soporte las características de multidifusión y descubrimiento de recursos.
- Utilizar la imagen Docker lista disponible en Docker Hub, que construye e inicia el servidor CoAP basado en el ejemplo de servidor multicast de Californium.
Uso del servidor de prueba basado en Docker
El siguiente comando extrae el contenedor docker para el servidor CoAP del Docker Hub y lo inicia:
docker run --name coap-multicast-server -d --rm --net=host tqtc/coap-multicast-test-server:californium-2.0.0
Nota: Puedes ejecutar más de un servidor CoAP multicast (en el mismo host o en otros hosts de la red) pasando un --name diferente al comando anterior.
Para terminar el contenedor docker después de su uso, primero obtenga el ID del contenedor ejecutando el comando docker ps. La salida tendrá este aspecto:
$ docker ps CONTAINER ID IMAGE 8b991fae7789 tqtc/coap-multicast-test-server:californium-2.0.0
Después, utiliza este ID para detener el contenedor:
docker stop <container_id>
Exponer clases C++ a QML
En este ejemplo, necesitas exponer las clases QCoapResource y QCoapClient, así como el espacio de nombres QtCoap, a QML. Para ello, crea clases envoltorio personalizadas y utiliza las macros de registro especiales.
Cree la clase QmlCoapResource como una envoltura alrededor de QCoapResource. Utilice la macro Q_PROPERTY para que varias propiedades sean accesibles desde QML. La clase no necesita ser directamente instanciable desde QML, así que utiliza la macro QML_ANONYMOUS para registrarla.
class QmlCoapResource : public QCoapResource { Q_GADGET Q_PROPERTY(QString title READ title) Q_PROPERTY(QString host READ hostStr) Q_PROPERTY(QString path READ path) QML_ANONYMOUS public: QmlCoapResource() : QCoapResource() {} QmlCoapResource(const QCoapResource &resource) : QCoapResource(resource) {} QString hostStr() const { return host().toString(); } };
A continuación, cree la clase QmlCoapMulticastClient con la clase QCoapClient como clase base. Utiliza la macro Q_PROPERTY para exponer una propiedad personalizada, y crea también varios métodos Q_INVOKABLE. Tanto la propiedad como los métodos invocables son accesibles desde QML. A diferencia de QmlCoapResource, quieres poder crear esta clase desde QML, así que utiliza la macro QML_NAMED_ELEMENT para registrar la clase en QML.
class QmlCoapMulticastClient : public QCoapClient { Q_OBJECT Q_PROPERTY(bool isDiscovering READ isDiscovering NOTIFY isDiscoveringChanged) QML_NAMED_ELEMENT(CoapMulticastClient) public: QmlCoapMulticastClient(QObject *parent = nullptr); Q_INVOKABLE void discover(const QString &host, int port, const QString &discoveryPath); Q_INVOKABLE void discover(QtCoap::MulticastGroup group, int port, const QString &discoveryPath); Q_INVOKABLE void stopDiscovery(); bool isDiscovering() const; Q_SIGNALS: void discovered(const QmlCoapResource &resource); void finished(int error); // The bool parameter is not provided, because the signal is only used by // the QML property system, and it does not use the passed value anyway. void isDiscoveringChanged(); public slots: void onDiscovered(QCoapResourceDiscoveryReply *reply, const QList<QCoapResource> &resources); private: QCoapResourceDiscoveryReply *m_reply = nullptr; };
Por último, registre el espacio de nombres QtCoap, para poder utilizar los enums que allí se proporcionan:
namespace QCoapForeignNamespace { Q_NAMESPACE QML_FOREIGN_NAMESPACE(QtCoap) QML_NAMED_ELEMENT(QtCoap) }
Ajuste de los archivos de compilación
Para que los tipos personalizados estén disponibles desde QML, actualice los archivos del sistema de compilación en consecuencia.
Compilación CMake
Para una compilación basada en CMake, añada lo siguiente a CMakeLists.txt:
qt_add_qml_module(quickmulticastclient
URI CoapClientModule
VERSION 1.0
SOURCES
qmlcoapmulticastclient.cpp qmlcoapmulticastclient.h
QML_FILES
Main.qml
)qmake Build
Para una compilación qmake, modifique el archivo quickmulticastclient.pro de la siguiente manera:
CONFIG += qmltypes
QML_IMPORT_NAME = CoapClientModule
QML_IMPORT_MAJOR_VERSION = 1
...
qml_resources.files = \
qmldir \
Main.qml
qml_resources.prefix = /qt/qml/CoapClientModule
RESOURCES += qml_resourcesUso de nuevos tipos QML
Ahora, cuando las clases C++ están correctamente expuestas a QML, puedes utilizar los nuevos tipos:
CoapMulticastClient { id: client onDiscovered: (resource) => { root.addResource(resource) } onFinished: (error) => { statusLabel.text = (error === QtCoap.Error.Ok) ? qsTr("Finished resource discovery.") : qsTr("Resource discovery failed with error code: %1").arg(error) } }
La señal QmlCoapMulticastClient::finished() activa el controlador de señales onFinished, para mostrar el estado de la solicitud en la interfaz de usuario. Tenga en cuenta que el ejemplo no utiliza directamente las señales de QCoapClient, ya que tanto error() como finished() toman como parámetro QCoapReply (que no está expuesto a QML), y el ejemplo sólo necesita el código de error.
El constructor de QmlCoapMulticastClient reenvía las señales de QCoapClient a la señal QmlCoapMulticastClient::finished():
QmlCoapMulticastClient::QmlCoapMulticastClient(QObject *parent) : QCoapClient(QtCoap::SecurityMode::NoSecurity, parent) { connect(this, &QCoapClient::finished, this, [this](QCoapReply *reply) { if (reply) { emit finished(static_cast<int>(reply->errorReceived())); reply->deleteLater(); if (m_reply == reply) { m_reply = nullptr; emit isDiscoveringChanged(); } } else { qCWarning(lcCoapClient, "Something went wrong, received a null reply"); } }); connect(this, &QCoapClient::error, this, [this](QCoapReply *, QtCoap::Error err) { emit finished(static_cast<int>(err)); }); }
Cuando se pulsa el botón Discover, se invoca uno de los métodos sobrecargados discover(), basado en el grupo de multidifusión seleccionado:
Button { id: discoverButton text: client.isDiscovering ? qsTr("Stop Discovery") : qsTr("Discover") Layout.preferredWidth: 100 onClicked: { if (client.isDiscovering) { client.stopDiscovery() } else { var currentGroup = groupComboBox.model.get(groupComboBox.currentIndex).value; var path = ""; if (currentGroup !== - 1) { client.discover(currentGroup, parseInt(portField.text), discoveryPathField.text); path = groupComboBox.currentText; } else { client.discover(customGroupField.text, parseInt(portField.text), discoveryPathField.text); path = customGroupField.text + discoveryPathField.text; } statusLabel.text = qsTr("Discovering resources at %1...").arg(path); } } }
Esta sobrecarga se invoca cuando se selecciona un grupo multicast personalizado o una dirección de host:
void QmlCoapMulticastClient::discover(const QString &host, int port, const QString &discoveryPath) { QUrl url; url.setHost(host); url.setPort(port); m_reply = QCoapClient::discover(url, discoveryPath); if (m_reply) { connect(m_reply, &QCoapResourceDiscoveryReply::discovered, this, &QmlCoapMulticastClient::onDiscovered); emit isDiscoveringChanged(); } else { qCWarning(lcCoapClient, "Discovery request failed."); } }
Y esta sobrecarga es llamada cuando uno de los grupos multicast sugeridos es seleccionado en la UI:
void QmlCoapMulticastClient::discover(QtCoap::MulticastGroup group, int port, const QString &discoveryPath) { m_reply = QCoapClient::discover(group, port, discoveryPath); if (m_reply) { connect(m_reply, &QCoapResourceDiscoveryReply::discovered, this, &QmlCoapMulticastClient::onDiscovered); emit isDiscoveringChanged(); } else { qCWarning(lcCoapClient, "Discovery request failed."); } }
La señal QCoapResourceDiscoveryReply::discovered() entrega una lista de QCoapResources, que no es un tipo QML. Para que los recursos estén disponibles en QML, reenvíe cada recurso de la lista a la señal QmlCoapMulticastClient::discovered(), que en su lugar toma un QmlCoapResource:
void QmlCoapMulticastClient::onDiscovered(QCoapResourceDiscoveryReply *reply, const QList<QCoapResource> &resources) { Q_UNUSED(reply) for (const auto &resource : resources) emit discovered(resource); }
Los recursos descubiertos se añaden a resourceModel de la vista de lista en la interfaz de usuario:
function addResource(resource) { resourceModel.insert(0, {"host" : resource.host, "path" : resource.path, "title" : resource.title}) }
Mientras el descubrimiento está en curso, pulse el botón Stop Discovery para detener el descubrimiento. Internamente se hace abortando la petición en curso:
void QmlCoapMulticastClient::stopDiscovery() { if (m_reply) m_reply->abortRequest(); }
Archivos:
© 2026 The Qt Company Ltd. Documentation contributions included herein are the copyrights of their respective owners. The documentation provided herein is licensed under the terms of the GNU Free Documentation License version 1.3 as published by the Free Software Foundation. Qt and respective logos are trademarks of The Qt Company Ltd. in Finland and/or other countries worldwide. All other trademarks are property of their respective owners.