Serveur DTLS
Cet exemple montre comment implémenter un serveur DTLS simple.

Remarque : l'exemple de serveur DTLS est destiné à être exécuté conjointement avec l'exemple de client DTLS.
Le serveur est implémenté par la classe DtlsServer. Il utilise les signaux QUdpSocket, QDtlsClientVerifier et QDtls pour tester l'accessibilité de chaque client, effectuer une poignée de main et lire et écrire des messages chiffrés.
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)
};Le constructeur relie le signal QUdpSocket::readyRead() à son slot readyRead() et définit la configuration TLS minimale requise :
DtlsServer::DtlsServer()
{
connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead);
serverConfiguration = QSslConfiguration::defaultDtlsConfiguration();
serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server");
serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);
}Remarque : le serveur n’utilise pas de certificat et s’appuie sur une poignée de main par clé pré-partagée (PSK).
La fonction `listen()` lie ` 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;
}Le slot `readyRead()` traite les datagrammes entrants :
...
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);
...Après avoir extrait une adresse et un numéro de port, le serveur vérifie d’abord s’il s’agit d’un datagramme provenant d’un pair déjà connu :
...
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;
});
...S’il s’agit d’une nouvelle adresse et d’un nouveau port inconnus, le datagramme est traité comme un message ClientHello potentiel, envoyé par un client DTLS :
...
if (client == knownClients.end())
return handleNewConnection(peerAddress, peerPort, dgram);
...S’il s’agit d’un client DTLS connu, le serveur déchiffre le datagramme :
...
if ((*client)->isConnectionEncrypted()) {
decryptDatagram(client->get(), dgram);
if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError)
knownClients.erase(client);
return;
}
...soit poursuit la procédure d’établissement de connexion avec ce pair :
...
doHandshake(client->get(), dgram);
...La fonction `handleNewConnection()` vérifie qu’il s’agit d’un client DTLS joignable, ou envoie une requête `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 le nouveau client a été identifié comme un client DTLS joignable, le serveur crée et configure un nouvel objet ` QDtls `, puis lance une procédure d'établissement de connexion côté serveur :
...
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() parcourt les différentes étapes de la phase d’établissement de la connexion :
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();
}
}Au cours de la phase de négociation, le signal ` QDtls::pskRequired()` est émis et le slot `pskRequired()` fournit la clé pré-partagée :
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"));
}Remarque : par souci de concision, la définition de pskRequired() est simplifiée à l’extrême. La documentation de la classe QSslPreSharedKeyAuthenticator explique en détail comment ce slot peut être correctement implémenté.
Une fois la négociation terminée pour le pair réseau, une connexion DTLS chiffrée est considérée comme établie et le serveur déchiffre les datagrammes suivants, envoyés par le pair, en appelant decryptDatagram(). Le serveur envoie également une réponse chiffrée au pair :
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());
}
}Le serveur ferme ses connexions DTLS en appelant QDtls::shutdown() :
void DtlsServer::shutdown()
{
for (const auto &connection : std::exchange(knownClients, {}))
connection->shutdown(&serverSocket);
serverSocket.close();
}Au cours de son fonctionnement, le serveur signale les erreurs, les messages d’information et les datagrammes déchiffrés en émettant les signaux `errorMessage()`, `warningMessage()`, `infoMessage()` et `datagramReceived()`. Ces messages sont consignés par l’interface utilisateur du serveur :
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.