简单的 CoAP 客户端
创建一个与 CoAP 服务器通信的应用程序。

“简单 CoAP 客户端”演示了如何创建一个最简化的 CoAP 客户端应用程序,用于发送和接收 CoAP 消息。
运行示例
您可以通过以下方式运行该示例:
- 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 服务器。您有以下选项:
- 使用位于
coap://coap.me的 CoAP 测试服务器。 - 使用libcoap、FreeCoAP或任何其他 CoAP 服务器实现来创建 CoAP 服务器。
- 使用Californiumplugtest 服务器,该服务器支持大多数 CoAP 功能。您可以手动构建该服务器,或使用现成的 Docker 镜像——该镜像会自动构建并启动 plugtest 服务器。以下将介绍使用基于 Docker 的服务器的步骤。
使用基于 Docker 的测试服务器
以下命令从 Docker Hub 拉取 CoAP 服务器的 Docker 容器并启动它:
docker run --name coap-test-server -d --rm -p 5683:5683/udp -p 5684:5684/udp tqtc/coap-californium-test-server:3.8.0要查询 Docker 容器的 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 地址访问 CoAP 测试服务器,端口分别为5683(非安全)和5684(安全)。
使用完毕后,若要终止 Docker 容器,请执行以下命令:
docker stop <container_id>此处的 `<container_id> ` 即为通过 `docker ps ` 命令获取的容器 ID。
创建客户端
第一步是使用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;
}
...对于PUT 和POST 请求,我们还会将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);
...客户端可以通过处理cancelObserveButton 的clicked() 信号来取消订阅该资源的监听:
...
connect(ui->cancelObserveButton, &QPushButton::clicked, this, [this, url]() {
m_client->cancelObserve(url);
ui->cancelObserveButton->setEnabled(false);
});来自服务器的响应将显示在用户界面中:
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;
}文件:
© 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.