Einfacher CoAP-Client

Erstellen einer Anwendung, die mit einem CoAP-Server kommuniziert.

Simple CoAP Client zeigt, wie man eine minimalistische CoAP-Client-Anwendung zum Senden und Empfangen von CoAP-Nachrichten erstellt.

Ausführen des Beispiels

Zum Ausführen des Beispiels von Qt Creatorzu starten, öffnen Sie den Modus Welcome und wählen Sie das Beispiel unter Examples aus. Weitere Informationen finden Sie unter Erstellen und Ausführen eines Beispiels.

Einrichten eines CoAP-Servers

Um die Anwendung zu verwenden, müssen Sie einen CoAP-Server angeben. Sie haben die folgenden Möglichkeiten:

  • Verwenden Sie den CoAP-Testserver unter coap://coap.me.
  • Erstellen Sie einen CoAP-Server mit libcoap, FreeCoAP oder einer anderen CoAP-Server-Implementierung.
  • Verwenden Sie den Californium Plugtest-Server, der die meisten CoAP-Funktionen unterstützt. Sie können ihn manuell erstellen oder ein fertiges Docker-Image verwenden, das den Plugtest-Server erstellt und startet. Die Schritte zur Verwendung des Docker-basierten Servers werden im Folgenden beschrieben.
Verwenden des Docker-basierten Test-Servers

Der folgende Befehl zieht den Docker-Container für den CoAP-Server aus dem Docker Hub und startet ihn:

docker run --name coap-test-server -d --rm -p 5683:5683/udp -p 5684:5684/udp tqtc/coap-californium-test-server:3.8.0

Um die IP-Adresse des Docker-Containers herauszufinden, rufen Sie zunächst die Container-ID durch Ausführen von docker ps ab, was eine Ausgabe wie diese ergibt:

$ docker ps
CONTAINER ID        IMAGE
5e46502df88f        tqtc/coap-californium-test-server:3.8.0

Anschließend können Sie die IP-Adresse mit dem folgenden Befehl abrufen:

docker inspect <container_id> | grep IPAddress

Zum Beispiel:

$ docker inspect 5e46502df88f | grep IPAddress
...
"IPAddress": "172.17.0.2",
...

Der CoAP-Testserver ist über die ermittelte IP-Adresse auf den Ports 5683 (nicht sicher) und 5684 (sicher) erreichbar.

Um den Docker-Container nach der Verwendung zu beenden, verwenden Sie den folgenden Befehl:

docker stop <container_id>

Die <container_id> ist hier die gleiche wie die, die mit dem Befehl docker ps abgerufen wird.

Erstellen eines Clients

Der erste Schritt besteht darin, einen CoAP-Client mit der Klasse QCoapClient zu erstellen. Dann müssen wir seine Signale verbinden, um benachrichtigt zu werden, wenn eine CoAP-Antwort empfangen wird oder eine Anfrage fehlgeschlagen ist:

MainWindow::MainWindow(QWidget *parent) :
    QMainWindow(parent),
    ui(new Ui::MainWindow)
{
    m_client = new QCoapClient(QtCoap::SecurityMode::NoSecurity, this);
    connect(m_client, &QCoapClient::finished, this, &MainWindow::onFinished);
    connect(m_client, &QCoapClient::error, this, &MainWindow::onError);
    ...

Senden von Anfragen

Wir verwenden die Klasse QCoapRequest, um CoAP-Anfragen zu erstellen. Diese Klasse bietet Methoden zur Erstellung von CoAP-Frames.

void MainWindow::on_runButton_clicked()
{
    const auto msgType = ui->msgTypeCheckBox->isChecked() ? QCoapMessage::Type::Confirmable
                                                          : QCoapMessage::Type::NonConfirmable;
    QUrl url;
    url.setHost(tryToResolveHostName(ui->hostComboBox->currentText()));
    url.setPort(ui->portSpinBox->value());
    url.setPath(ui->resourceComboBox->currentText());

    QCoapRequest request(url, msgType);
    for (const auto &option : std::as_const(m_options))
        request.addOption(option);
    ...

In diesem Beispiel legen wir die URL sowie den Nachrichtentyp fest und fügen der Anfrage Optionen hinzu. Es ist auch möglich, die Nutzlast, die Nachrichten-ID, das Token usw. festzulegen, aber wir verwenden hier die Standardwerte. Beachten Sie, dass die Nachrichten-ID und das Token standardmäßig nach dem Zufallsprinzip generiert werden.

Je nach gewählter Anfragemethode senden wir eine GET, PUT, POST oder DELETE Anfrage an den Server:

    ...
    switch (method) {
    case QtCoap::Method::Get:
        m_client->get(request);
        break;
    case QtCoap::Method::Put:
        m_client->put(request, m_currentData);
        break;
    case QtCoap::Method::Post:
        m_client->post(request, m_currentData);
        break;
    case QtCoap::Method::Delete:
        m_client->deleteResource(request);
        break;
    default:
        break;
    }
    ...

Bei den Anfragen PUT und POST fügen wir auch m_currentData als Nutzdaten für die Anfrage hinzu.

Um den Inhalt des Servers zu durchsuchen und die auf ihm verfügbaren Ressourcen zu ermitteln, wird eine Discovery-Anfrage verwendet:

void MainWindow::on_discoverButton_clicked()
{
    ...
    QCoapResourceDiscoveryReply *discoverReply =
            m_client->discover(url, ui->discoveryPathEdit->text());
    if (discoverReply) {
        connect(discoverReply, &QCoapResourceDiscoveryReply::discovered,
                this, &MainWindow::onDiscovered);
    ...

Hinweis: Anstelle der Klasse QCoapReply verwenden wir die Klasse QCoapResourceDiscoveryReply, um die Antwort auf eine Discovery-Anfrage zu speichern. Sie hat das Signal QCoapResourceDiscoveryReply::discovered, das die Liste der entdeckten QCoapResources zurückgibt.

Wenn es auf dem Server beobachtbare Ressourcen gibt (d. h. sie haben den Ressourcentyp obs), können wir Aktualisierungen dieser Ressource abonnieren, indem wir eine Beobachtungsanforderung ausführen:

void MainWindow::on_observeButton_clicked()
{
    ...
    QCoapReply *observeReply = m_client->observe(url);
    ...
    connect(observeReply, &QCoapReply::notified, this, &MainWindow::onNotified);
    ...

Der Client kann sich von der Ressourcenbeobachtung abmelden, indem er das Signal clicked() der cancelObserveButton verarbeitet:

    ...
    connect(ui->cancelObserveButton, &QPushButton::clicked, this, [this, url]() {
        m_client->cancelObserve(url);
        ui->cancelObserveButton->setEnabled(false);
    });

Die vom Server kommenden Antworten werden in der Benutzeroberfläche angezeigt:

void MainWindow::addMessage(const QString &message, bool isError)
{
    const QString content = "--------------- %1 ---------------\n%2\n\n"_L1
                                .arg(QDateTime::currentDateTime().toString(), message);
    ui->textEdit->setTextColor(isError ? Qt::red : Qt::black);
    ui->textEdit->insertPlainText(content);
    ui->textEdit->ensureCursorVisible();
}

void MainWindow::onFinished(QCoapReply *reply)
{
    if (reply->errorReceived() == QtCoap::Error::Ok)
        addMessage(reply->message().payload());
}

static QString errorMessage(QtCoap::Error errorCode)
{
    const auto error = QMetaEnum::fromType<QtCoap::Error>().valueToKey(static_cast<int>(errorCode));
    return MainWindow::tr("Request failed with error: %1\n").arg(error);
}

void MainWindow::onError(QCoapReply *reply, QtCoap::Error error)
{
    const auto errorCode = reply ? reply->errorReceived() : error;
    addMessage(errorMessage(errorCode), true);
}

void MainWindow::onDiscovered(QCoapResourceDiscoveryReply *reply, QList<QCoapResource> resources)
{
    if (reply->errorReceived() != QtCoap::Error::Ok)
        return;

    QString message;
    for (const auto &resource : std::as_const(resources)) {
        ui->resourceComboBox->addItem(resource.path());
        message += tr("Discovered resource: \"%1\" on path %2\n")
                        .arg(resource.title(), resource.path());
    }
    addMessage(message);
}

void MainWindow::onNotified(QCoapReply *reply, const QCoapMessage &message)
{
    if (reply->errorReceived() == QtCoap::Error::Ok) {
        addMessage(tr("Received observe notification with payload: %1")
                        .arg(QString::fromUtf8(message.payload())));
    }
}

static QString tryToResolveHostName(const QString hostName)
{
    const auto hostInfo = QHostInfo::fromName(hostName);
    if (!hostInfo.addresses().empty())
        return hostInfo.addresses().first().toString();

    return hostName;
}

Dateien:

© 2025 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.