간단한 CoAP 클라이언트

CoAP 서버와 통신하는 애플리케이션 만들기.

간단한 CoAP 클라이언 트는 CoAP 메시지를 보내고 받을 수 있는 최소한의 CoAP 클라이언트 애플리케이션을 만드는 방법을 보여줍니다.

예제 실행하기

에서 예제를 실행하려면 Qt Creator에서 Welcome 모드를 열고 Examples 에서 예제를 선택합니다. 자세한 내용은 예제 빌드 및 실행하기를 참조하세요.

CoAP 서버 설정하기

애플리케이션을 사용하려면 CoAP 서버를 지정해야 합니다. 다음과 같은 옵션이 있습니다:

  • coap://coap.me 에 있는 CoAP 테스트 서버를 사용합니다.
  • libcoap, FreeCoAP 또는 기타 CoAP 서버 구현을 사용하여 CoAP 서버를 만듭니다.
  • 대부분의 CoAP 기능을 지원하는 캘리포니아 플러그테스트 서버를 사용합니다. 수동으로 빌드하거나 플러그테스트 서버를 빌드하고 시작하는 준비된 Docker 이미지를 사용할 수 있습니다. Docker 기반 서버를 사용하는 단계는 아래에 설명되어 있습니다.
도커 기반 테스트 서버 사용하기

다음 명령은 도커 허브에서 CoAP 서버용 도커 컨테이너를 가져와서 시작합니다:

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

도커 컨테이너의 IP 주소를 찾으려면 먼저 docker ps 을 실행하여 컨테이너 ID를 검색하면 다음과 같은 내용이 출력됩니다:

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

그런 다음 다음 명령을 사용하여 IP 주소를 얻을 수 있습니다:

docker inspect <container_id> | grep IPAddress

예를 들어

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

검색된 IP 주소로 포트 5683 (비보안) 및 5684 (보안)에서 CoAP 테스트 서버에 연결할 수 있습니다.

사용 후 도커 컨테이너를 종료하려면 다음 명령을 사용합니다:

docker stop <container_id>

여기서 <container_id>docker ps 명령으로 검색한 것과 동일합니다.

클라이언트 만들기

첫 번째 단계는 QCoapClient 클래스를 사용하여 CoAP 클라이언트를 만드는 것입니다. 그런 다음 CoAP 응답이 수신되거나 요청이 실패했을 때 알림을 받으려면 신호를 연결해야 합니다:

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);
    ...

요청 보내기

QCoapRequest 클래스를 사용하여 CoAP 요청을 생성합니다. 이 클래스는 CoAP 프레임을 구성하는 메서드를 제공합니다.

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);
    ...

이 예에서는 URL과 메시지 유형을 설정하고 요청에 옵션을 추가합니다. 페이로드, 메시지 ID, 토큰 등을 설정할 수도 있지만 여기서는 기본값을 사용하고 있습니다. 기본적으로 메시지 ID와 토큰은 무작위로 생성된다는 점에 유의하세요.

선택한 요청 방법에 따라 GET, PUT, POST 또는 DELETE 요청을 서버로 보냅니다:

    ...
    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;
    }
    ...

PUTPOST 요청의 경우 요청에 대한 페이로드로 m_currentData 도 추가합니다.

서버의 콘텐츠를 탐색하고 서버에서 사용 가능한 리소스를 검색하기 위해 검색 요청이 사용됩니다:

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

참고: QCoapReply 클래스 대신 QCoapResourceDiscoveryReply 클래스를 사용하여 검색 요청에 대한 응답을 보관합니다. 여기에는 QCoapResourceDiscoveryReply::discovered 신호가 있으며, 이 신호는 검색된 QCoapResources의 목록을 반환합니다.

서버에 관찰 가능한 리소스가 있는 경우(즉, 리소스 유형이 obs)에는 관찰 요청을 실행하여 해당 리소스에 대한 업데이트를 구독할 수 있습니다:

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

클라이언트는 cancelObserveButtonclicked() 신호를 처리하여 리소스 관찰을 구독 취소할 수 있습니다:

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

서버에서 오는 응답은 UI에 표시됩니다:

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;
}

파일:

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