간편한 CoAP 멀티캐스트 검색
Qt Quick 사용자 인터페이스를 사용하여 CoAP 클라이언트로 멀티캐스트 리소스를 탐색하는 방법.

'빠른 CoAP 멀티캐스트 검색' 예제는 QCoapClient 를 QML 유형으로 등록하고, Qt Quick 애플리케이션에서 CoAP 멀티캐스트 리소스 검색에 이를 사용하는 방법을 보여줍니다.
참고: Qt CoAP 는 현재 버전에서 QML API를 제공하지 않습니다. 하지만 이 예제에서 보여주는 것처럼, 모듈의 C++ 클래스를 QML에서 사용할 수 있도록 설정할 수 있습니다.
예제 실행
다음 경로에서 예제를 실행할 수 있습니다.
- Qt Creator
Welcome 모드를 열고 Examples 에서 예제를 선택하십시오. 자세한 내용은 Qt Creator: 튜토리얼: 빌드 및 실행을 참조하십시오.
- Qt Extension for Visual Studio Code
Command Palette 에서 Qt: Open Qt examples 명령을 실행하고 목록에서 예제를 선택합니다. 자세한 내용은 Qt Extension for Visual Studio Code: 튜토리얼: 빌드 및 실행을 참조하십시오.
CoAP 서버 설정
예제 애플리케이션을 실행하려면 먼저 멀티캐스트 리소스 검색을 지원하는 CoAP 서버를 하나 이상 설정하고 시작해야 합니다. 다음과 같은 옵션이 있습니다.
- libcoap, Californium 또는 멀티캐스트 및 리소스 검색 기능을 지원하는 기타 CoAP 서버 구현체를 사용하여 CoAP 서버를 수동으로 빌드하고 실행합니다.
- Docker Hub에서 제공되는 기성 Docker 이미지를 사용합니다. 이 이미지는 Californium의 멀티캐스트 서버 예제를 기반으로 CoAP 서버를 빌드하고 시작합니다.
Docker 기반 테스트 서버 사용
다음 명령어를 실행하면 Docker Hub에서 CoAP 서버용 Docker 컨테이너를 가져와 시작합니다:
docker run --name coap-multicast-server -d --rm --net=host tqtc/coap-multicast-test-server:californium-2.0.0참고: 위 명령어에 서로 다른 --name 를 전달하여 (동일한 호스트 또는 네트워크 내의 다른 호스트에서) 하나 이상의 멀티캐스트 CoAP 서버를 실행할 수있습니다 .
사용 후 도커 컨테이너를 종료하려면, 먼저 docker ps 명령어를 실행하여 컨테이너의 ID를 확인하십시오. 출력은 다음과 같습니다:
$ docker ps
CONTAINER ID IMAGE
8b991fae7789 tqtc/coap-multicast-test-server:californium-2.0.0그 후, 이 ID를 사용하여 컨테이너를 중지합니다:
docker stop <container_id>C++ 클래스를 QML에 노출하기
이 예제에서는 QCoapResource 및 QCoapClient 클래스와 QtCoap 네임스페이스를 QML에 노출해야 합니다. 이를 위해 사용자 정의 래퍼 클래스를 생성하고 특수 등록 매크로를 사용합니다.
` QCoapResource`을 감싸는 래퍼 클래스로 ` QmlCoapResource ` 클래스를 생성합니다. ` Q_PROPERTY ` 매크로를 사용하여 QML에서 여러 속성에 접근할 수 있도록 합니다. 이 클래스는 QML에서 직접 인스턴스화할 필요가 없으므로, ` QML_ANONYMOUS ` 매크로를 사용하여 등록합니다.
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(); }
};그 후, QCoapClient 클래스를 기본 클래스로 하여 QmlCoapMulticastClient 클래스를 생성합니다. Q_PROPERTY 매크로를 사용하여 사용자 정의 속성을 노출하고, Q_INVOKABLE 메서드도 여러 개 생성합니다. 속성과 호출 가능한 메서드 모두 QML에서 액세스할 수 있습니다. QmlCoapResource 와 달리, 이 클래스는 QML에서 생성할 수 있어야 하므로 QML_NAMED_ELEMENT 매크로를 사용하여 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;
};마지막으로, QtCoap 네임스페이스를 등록하여 해당 네임스페이스에서 제공하는 열거형을 사용할 수 있도록 합니다:
namespace QCoapForeignNamespace
{
Q_NAMESPACE
QML_FOREIGN_NAMESPACE(QtCoap)
QML_NAMED_ELEMENT(QtCoap)
}빌드 파일 조정
QML에서 사용자 정의 유형을 사용할 수 있도록 하려면 빌드 시스템 파일을 적절히 업데이트하십시오.
CMake 빌드
CMake 기반 빌드의 경우, CMakeLists.txt 파일에 다음 내용을 추가하십시오:
qt_add_qml_module(quickmulticastclient
URI CoapClientModule
VERSION 1.0
SOURCES
qmlcoapmulticastclient.cpp qmlcoapmulticastclient.h
QML_FILES
Main.qml
)qmake 빌드
qmake 빌드의 경우, ` quickmulticastclient.pro ` 파일을 다음과 같이 수정하십시오:
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_resources새로운 QML 유형 사용
이제 C++ 클래스가 QML에 올바르게 노출되었으므로 새로운 유형을 사용할 수 있습니다:
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)
}
}QmlCoapMulticastClient::finished() 신호는 onFinished 신호 핸들러를 트리거하여 UI에 요청 상태를 표시합니다. 이 예제에서는 QCoapClient 의 신호를 직접 사용하지 않는다는 점에 유의하십시오. error()와 finished() 신호 모두 QCoapReply 를 매개변수로 받는데(이는 QML에 노출되지 않음), 이 예제에서는 오류 코드만 필요하기 때문입니다.
QmlCoapMulticastClient 의 생성자는 QCoapClient 의 시그널을 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));
});
}Discover 버튼이 눌리면, 선택된 멀티캐스트 그룹에 따라 discover() 의 오버로드된 메서드 중 하나가 호출됩니다:
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);
}
}
}사용자 정의 멀티캐스트 그룹이나 호스트 주소가 선택되면 이 오버로드가 호출됩니다:
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.");
}
}그리고 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.");
}
}QCoapResourceDiscoveryReply::discovered() 신호는 QML 유형이 아닌 QCoapResource목록을 전달합니다. QML에서 리소스를 사용할 수 있게 하려면, 목록에 있는 각 리소스를 QmlCoapResource 를 매개변수로 받는 QmlCoapMulticastClient::discovered() 신호로 전달해야 합니다:
void QmlCoapMulticastClient::onDiscovered(QCoapResourceDiscoveryReply *reply,
const QList<QCoapResource> &resources)
{
Q_UNUSED(reply)
for (const auto &resource : resources)
emit discovered(resource);
}탐지된 리소스는 UI의 목록 뷰( resourceModel )에 추가됩니다:
function addResource(resource) {
resourceModel.insert(0, {"host" : resource.host,
"path" : resource.path,
"title" : resource.title})
}탐색이 진행 중일 때, Stop Discovery 버튼을 눌러 탐색을 중지할 수 있습니다. 내부적으로는 현재 요청을 중단하는 방식으로 처리됩니다:
void QmlCoapMulticastClient::stopDiscovery()
{
if (m_reply)
m_reply->abortRequest();
}파일:
© 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.