Servidor DTLS
Este ejemplo muestra cómo implementar un servidor DTLS simple.

Nota: El ejemplo de servidor DTLS está pensado para ser ejecutado junto con el ejemplo de cliente DTLS.
El servidor está implementado por la clase DtlsServer. Utiliza QUdpSocket, QDtlsClientVerifier, y QDtls para comprobar la accesibilidad de cada cliente, completar un handshake, y leer y escribir mensajes encriptados.
class DtlsServer : public QObject { Q_OBJECT public: DtlsServer(); ~DtlsServer(); bool listen(const QHostAddress &address, quint16 port); bool isListening() const; void close(); signals: void errorMessage(const QString &message); void warningMessage(const QString &message); void infoMessage(const QString &message); void datagramReceived(const QString &peerInfo, const QByteArray &cipherText, const QByteArray &plainText); private slots: void readyRead(); void pskRequired(QSslPreSharedKeyAuthenticator *auth); private: void handleNewConnection(const QHostAddress &peerAddress, quint16 peerPort, const QByteArray &clientHello); void doHandshake(QDtls *newConnection, const QByteArray &clientHello); void decryptDatagram(QDtls *connection, const QByteArray &clientMessage); void shutdown(); bool listening = false; QUdpSocket serverSocket; QSslConfiguration serverConfiguration; QDtlsClientVerifier cookieSender; std::vector<std::unique_ptr<QDtls>> knownClients; Q_DISABLE_COPY(DtlsServer) };
El constructor conecta la señal QUdpSocket::readyRead() a su slot readyRead() y establece la configuración TLS mínima necesaria:
DtlsServer::DtlsServer() { connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead); serverConfiguration = QSslConfiguration::defaultDtlsConfiguration(); serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server"); serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone); }
Nota: El servidor no utiliza un certificado y confía en el handshake de clave pre-compartida (PSK).
listen() enlaza QUdpSocket:
bool DtlsServer::listen(const QHostAddress &address, quint16 port) { if (address != serverSocket.localAddress() || port != serverSocket.localPort()) { shutdown(); listening = serverSocket.bind(address, port); if (!listening) emit errorMessage(serverSocket.errorString()); } else { listening = true; } return listening; }
La ranura readyRead() procesa los datagramas entrantes:
... const qint64 bytesToRead = serverSocket.pendingDatagramSize(); if (bytesToRead <= 0) { emit warningMessage(tr("A spurious read notification")); return; } QByteArray dgram(bytesToRead, Qt::Uninitialized); QHostAddress peerAddress; quint16 peerPort = 0; const qint64 bytesRead = serverSocket.readDatagram(dgram.data(), dgram.size(), &peerAddress, &peerPort); if (bytesRead <= 0) { emit warningMessage(tr("Failed to read a datagram: ") + serverSocket.errorString()); return; } dgram.resize(bytesRead); ...
Tras extraer una dirección y un número de puerto, el servidor comprueba primero si se trata de un datagrama de un par ya conocido:
... if (peerAddress.isNull() || !peerPort) { emit warningMessage(tr("Failed to extract peer info (address, port)")); return; } const auto client = std::find_if(knownClients.begin(), knownClients.end(), [&](const std::unique_ptr<QDtls> &connection){ return connection->peerAddress() == peerAddress && connection->peerPort() == peerPort; }); ...
Si se trata de una dirección y un puerto nuevos y desconocidos, el datagrama se procesa como un posible mensaje ClientHello, enviado por un cliente DTLS:
... if (client == knownClients.end()) return handleNewConnection(peerAddress, peerPort, dgram); ...
Si se trata de un cliente DTLS conocido, el servidor descifra el datagrama:
... if ((*client)->isConnectionEncrypted()) { decryptDatagram(client->get(), dgram); if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError) knownClients.erase(client); return; } ...
o continúa un handshake con este peer:
... doHandshake(client->get(), dgram); ...
handleNewConnection() verifica que es un cliente DTLS alcanzable, o envía un HelloVerifyRequest:
void DtlsServer::handleNewConnection(const QHostAddress &peerAddress, quint16 peerPort, const QByteArray &clientHello) { if (!listening) return; const QString peerInfo = peer_info(peerAddress, peerPort); if (cookieSender.verifyClient(&serverSocket, clientHello, peerAddress, peerPort)) { emit infoMessage(peerInfo + tr(": verified, starting a handshake")); ...
Si se verifica que el nuevo cliente es un cliente DTLS alcanzable, el servidor crea y configura un nuevo objeto QDtls, e inicia un handshake del lado del servidor:
...
std::unique_ptr<QDtls> newConnection{new QDtls{QSslSocket::SslServerMode}};
newConnection->setDtlsConfiguration(serverConfiguration);
newConnection->setPeer(peerAddress, peerPort);
newConnection->connect(newConnection.get(), &QDtls::pskRequired,
this, &DtlsServer::pskRequired);
knownClients.push_back(std::move(newConnection));
doHandshake(knownClients.back().get(), clientHello);
...doHandshake() avanza a través de la fase de handshake:
void DtlsServer::doHandshake(QDtls *newConnection, const QByteArray &clientHello) { const bool result = newConnection->doHandshake(&serverSocket, clientHello); if (!result) { emit errorMessage(newConnection->dtlsErrorString()); return; } const QString peerInfo = peer_info(newConnection->peerAddress(), newConnection->peerPort()); switch (newConnection->handshakeState()) { case QDtls::HandshakeInProgress: emit infoMessage(peerInfo + tr(": handshake is in progress ...")); break; case QDtls::HandshakeComplete: emit infoMessage(tr("Connection with %1 encrypted. %2") .arg(peerInfo, connection_info(newConnection))); break; default: Q_UNREACHABLE(); } }
Durante la fase de handshake, se emite la señal QDtls::pskRequired() y la ranura pskRequired() proporciona la clave precompartida:
void DtlsServer::pskRequired(QSslPreSharedKeyAuthenticator *auth) { Q_ASSERT(auth); emit infoMessage(tr("PSK callback, received a client's identity: '%1'") .arg(QString::fromLatin1(auth->identity()))); auth->setPreSharedKey(QByteArrayLiteral("\x1a\x2b\x3c\x4d\x5e\x6f")); }
Nota: En aras de la brevedad, la definición de pskRequired() está simplificada en exceso. La documentación para la clase QSslPreSharedKeyAuthenticator explica en detalle cómo esta ranura puede ser implementada apropiadamente.
Una vez completado el handshake para el peer de la red, se considera establecida una conexión DTLS encriptada y el servidor desencripta los datagramas subsiguientes, enviados por el peer, llamando a decryptDatagram(). El servidor también envía una respuesta cifrada al peer:
void DtlsServer::decryptDatagram(QDtls *connection, const QByteArray &clientMessage) { Q_ASSERT(connection->isConnectionEncrypted()); const QString peerInfo = peer_info(connection->peerAddress(), connection->peerPort()); const QByteArray dgram = connection->decryptDatagram(&serverSocket, clientMessage); if (dgram.size()) { emit datagramReceived(peerInfo, clientMessage, dgram); connection->writeDatagramEncrypted(&serverSocket, tr("to %1: ACK").arg(peerInfo).toLatin1()); } else if (connection->dtlsError() == QDtlsError::NoError) { emit warningMessage(peerInfo + ": " + tr("0 byte dgram, could be a re-connect attempt?")); } else { emit errorMessage(peerInfo + ": " + connection->dtlsErrorString()); } }
El servidor cierra sus conexiones DTLS llamando a QDtls::shutdown():
void DtlsServer::shutdown() { for (const auto &connection : std::exchange(knownClients, {})) connection->shutdown(&serverSocket); serverSocket.close(); }
Durante su funcionamiento, el servidor informa de errores, mensajes informativos y datagramas descifrados, emitiendo señales errorMessage(), warningMessage(), infoMessage() y datagramReceived(). Estos mensajes son registrados por la interfaz de usuario del servidor:
const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>")); void MainWindow::addErrorMessage(const QString &message) { ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message)); } void MainWindow::addWarningMessage(const QString &message) { ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message)); } void MainWindow::addInfoMessage(const QString &message) { ui->serverInfo->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message)); } void MainWindow::addClientMessage(const QString &peerInfo, const QByteArray &datagram, const QByteArray &plainText) { static const QString messageColor = QStringLiteral("DarkMagenta"); static const QString formatter = QStringLiteral("<br>---------------" "<br>A message from %1" "<br>DTLS datagram:<br> %2" "<br>As plain text:<br> %3"); const QString html = formatter.arg(peerInfo, QString::fromUtf8(datagram.toHex(' ')), QString::fromUtf8(plainText)); ui->messages->insertHtml(colorizer.arg(messageColor, html)); }
© 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.