Client DTLS
Cet exemple montre comment implémenter des connexions DTLS côté client.

Remarque : l'exemple de client DTLS est destiné à être exécuté conjointement avec l'exemple de serveur DTLS.
L'exemple de client DTLS peut établir plusieurs connexions DTLS vers un ou plusieurs serveurs DTLS. Une connexion DTLS côté client est implémentée par la classe `DtlsAssociation`. Cette classe utilise ` QUdpSocket ` pour lire et écrire des datagrammes et ` QDtls ` pour le chiffrement :
class DtlsAssociation : public QObject
{
Q_OBJECT
public:
DtlsAssociation(const QHostAddress &address, quint16 port,
const QString &connectionName);
~DtlsAssociation();
void startHandshake();
signals:
void errorMessage(const QString &message);
void warningMessage(const QString &message);
void infoMessage(const QString &message);
void serverResponse(const QString &clientInfo, const QByteArray &datagraam,
const QByteArray &plainText);
private slots:
void udpSocketConnected();
void readyRead();
void handshakeTimeout();
void pskRequired(QSslPreSharedKeyAuthenticator *auth);
void pingTimeout();
private:
QString name;
QUdpSocket socket;
QDtls crypto;
QTimer pingTimer;
unsigned ping = 0;
Q_DISABLE_COPY(DtlsAssociation)
};Le constructeur définit la configuration TLS minimale pour la nouvelle connexion DTLS, et spécifie l’adresse et le port du serveur :
...
auto configuration = QSslConfiguration::defaultDtlsConfiguration();
configuration.setPeerVerifyMode(QSslSocket::VerifyNone);
crypto.setPeer(address, port);
crypto.setDtlsConfiguration(configuration);
...Le signal QDtls::handshakeTimeout() est connecté au slot handleTimeout() afin de gérer la perte de paquets et leur retransmission pendant la phase d’établissement de la connexion :
...
connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout);
...Pour nous assurer de ne recevoir que les datagrammes provenant du serveur, nous connectons notre socket UDP au serveur :
...
socket.connectToHost(address.toString(), port);
...Le signal ` QUdpSocket::readyRead()` est connecté au slot `readyRead()` :
...
connect(&socket, &QUdpSocket::readyRead, this, &DtlsAssociation::readyRead);
...Lorsqu’une connexion sécurisée avec un serveur est établie, un objet `DtlsAssociation` envoie de courts messages « ping » au serveur, à l’aide d’un minuteur :
pingTimer.setInterval(5000);
connect(&pingTimer, &QTimer::timeout, this, &DtlsAssociation::pingTimeout);La fonction `startHandshake()` lance une négociation de connexion avec le serveur :
void DtlsAssociation::startHandshake()
{
if (socket.state() != QAbstractSocket::ConnectedState) {
emit infoMessage(tr("%1: connecting UDP socket first ...").arg(name));
connect(&socket, &QAbstractSocket::connected, this, &DtlsAssociation::udpSocketConnected);
return;
}
if (!crypto.doHandshake(&socket))
emit errorMessage(tr("%1: failed to start a handshake - %2").arg(name, crypto.dtlsErrorString()));
else
emit infoMessage(tr("%1: starting a handshake").arg(name));
}Le slot `readyRead()` lit un datagramme envoyé par le serveur :
QByteArray dgram(socket.pendingDatagramSize(), Qt::Uninitialized);
const qint64 bytesRead = socket.readDatagram(dgram.data(), dgram.size());
if (bytesRead <= 0) {
emit warningMessage(tr("%1: spurious read notification?").arg(name));
return;
}
dgram.resize(bytesRead);Si la négociation de connexion est déjà terminée, ce datagramme est déchiffré :
if (crypto.isConnectionEncrypted()) {
const QByteArray plainText = crypto.decryptDatagram(&socket, dgram);
if (plainText.size()) {
emit serverResponse(name, dgram, plainText);
return;
}
if (crypto.dtlsError() == QDtlsError::RemoteClosedConnectionError) {
emit errorMessage(tr("%1: shutdown alert received").arg(name));
socket.close();
pingTimer.stop();
return;
}
emit warningMessage(tr("%1: zero-length datagram received?").arg(name));
} else {sinon, nous essayons de poursuivre la négociation :
if (!crypto.doHandshake(&socket, dgram)) {
emit errorMessage(tr("%1: handshake error - %2").arg(name, crypto.dtlsErrorString()));
return;
}Une fois la négociation terminée, nous envoyons notre premier message ping :
if (crypto.isConnectionEncrypted()) {
emit infoMessage(tr("%1: encrypted connection established!").arg(name));
pingTimer.start();
pingTimeout();
} else {Le slot pskRequired() fournit la clé pré-partagée (PSK) nécessaire pendant la phase d'établissement de la connexion :
void DtlsAssociation::pskRequired(QSslPreSharedKeyAuthenticator *auth)
{
Q_ASSERT(auth);
emit infoMessage(tr("%1: providing pre-shared key ...").arg(name));
auth->setIdentity(name.toLatin1());
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é.
pingTimeout() envoie un message chiffré au serveur :
void DtlsAssociation::pingTimeout()
{
static const QString message = QStringLiteral("I am %1, please, accept our ping %2");
const qint64 written = crypto.writeDatagramEncrypted(&socket, message.arg(name).arg(ping).toLatin1());
if (written <= 0) {
emit errorMessage(tr("%1: failed to send a ping - %2").arg(name, crypto.dtlsErrorString()));
pingTimer.stop();
return;
}
++ping;
}Au cours de la phase d’établissement de la connexion, le client doit gérer les éventuels délais d’expiration, qui peuvent survenir en raison de la perte de paquets. Le slot handshakeTimeout() retransmet les messages d’établissement de la connexion :
void DtlsAssociation::handshakeTimeout()
{
emit warningMessage(tr("%1: handshake timeout, trying to re-transmit").arg(name));
if (!crypto.handleTimeout(&socket))
emit errorMessage(tr("%1: failed to re-transmit - %2").arg(name, crypto.dtlsErrorString()));
}Avant qu’une connexion client ne soit interrompue, sa connexion DTLS doit être fermée :
DtlsAssociation::~DtlsAssociation()
{
if (crypto.isConnectionEncrypted())
crypto.shutdown(&socket);
}Les messages d'erreur, les messages d'information et les réponses déchiffrées provenant des serveurs sont affichés par l'interface utilisateur :
const QString colorizer(QStringLiteral("<font color=\"%1\">%2</font><br>"));
void MainWindow::addErrorMessage(const QString &message)
{
ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("Crimson"), message));
}
void MainWindow::addWarningMessage(const QString &message)
{
ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkOrange"), message));
}
void MainWindow::addInfoMessage(const QString &message)
{
ui->clientMessages->insertHtml(colorizer.arg(QStringLiteral("DarkBlue"), message));
}
void MainWindow::addServerResponse(const QString &clientInfo, const QByteArray &datagram,
const QByteArray &plainText)
{
static const QString messageColor = QStringLiteral("DarkMagenta");
static const QString formatter = QStringLiteral("<br>---------------"
"<br>%1 received a DTLS datagram:<br> %2"
"<br>As plain text:<br> %3");
const QString html = formatter.arg(clientInfo, QString::fromUtf8(datagram.toHex(' ')),
QString::fromUtf8(plainText));
ui->serverMessages->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.