Qt文档阅读笔记-DTLS client解析
发布日期:2021-06-30 10:47:29 浏览次数:3 分类:技术文章

本文共 6205 字,大约阅读时间需要 20 分钟。

此篇博文讲解了DTLS客户端的编写

注意:DTLS客户端需要结合DTLS服务端一起跑才有效果。

这里使用DTLS客户端使用少量的连接可以和一个或多个DTLS服务端进行通信。DtlsAssociation是DTLS客户端连接类。这个类使用了QudpSocket去进行数据报的读写,使用QDtls进行数据报的加密:

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)  };

构造函数中最小化配置了TLS,用于DTLS连接的创建,以及配置了IP地址和端口:

...  auto configuration = QSslConfiguration::defaultDtlsConfiguration();  configuration.setPeerVerifyMode(QSslSocket::VerifyNone);  crypto.setPeer(address, port);  crypto.setDtlsConfiguration(configuration);      ...

QDtls::handshakeTimeout()信号与handleTimeout()槽函数相关联,处理有问题的数据报,和握手时出现问题时数据的重发:

...  connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout);      ...

使用UDPSocket连接到服务端:

...  socket.connectToHost(address.toString(), port);      ...

QUdpSocket::readyRead()信号与readyRead()槽函数相关联

...  connect(&socket, &QUdpSocket::readyRead, this, &DtlsAssociation::readyRead);      ...

当一条加密的连接创建后,DtlsAssociation对象会发送一条简短的ping消息到服务端:

pingTimer.setInterval(5000);  connect(&pingTimer, &QTimer::timeout, this, &DtlsAssociation::pingTimeout);

startHandshake()函数用于与服务端的握手:

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));  }

readyRead()槽函数读取服务端传输过来的数据:

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);

如果握手已经完成,随后就是数据的加密了:

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 {

如果出现错误,就继续进行握手:

if (!crypto.doHandshake(&socket, dgram)) {          emit errorMessage(tr("%1: handshake error - %2").arg(name, crypto.dtlsErrorString()));          return;      }

当握手完成,就发送第一个ping消息:

if (crypto.isConnectionEncrypted()) {          emit infoMessage(tr("%1: encrypted connection established!").arg(name));          pingTimer.start();          pingTimeout();      } else {

pskRequired()槽函数在握手期间提供了Pre-Shared Key(PSK)

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"));  }

上面的代码是比较简洁的,QSslPreSharedKeyAuthenticator类讲解了如何正确的设置PSK及证书相关的设置。

pingTimeout()向服务端发送机密数据:

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;  }

在握手期间,客户端需要处理超时丢包的现象,这里使用handshakeTimeout()进行处理:

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()));  }

析构函数中销毁DTLS的连接:

DtlsAssociation::~DtlsAssociation()  {      if (crypto.isConnectionEncrypted())          crypto.shutdown(&socket);  }

错误消息,提示消息,收到服务端的返回值都展示到UI界面上了:

const QString colorizer(QStringLiteral("%2
")); 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("
---------------" "
%1 received a DTLS datagram:
%2" "
As plain text:
%3"); const QString html = formatter.arg(clientInfo, QString::fromUtf8(datagram.toHex(' ')), QString::fromUtf8(plainText)); ui->serverMessages->insertHtml(colorizer.arg(messageColor, html)); }

 

转载地址:https://it1995.blog.csdn.net/article/details/117701478 如侵犯您的版权,请留言回复原文章的地址,我们会给您删除此文章,给您带来不便请您谅解!

上一篇:Charles笔记-配置Charles代理抓取HTTP和HTTPS数据包,安卓模拟器连接Charles
下一篇:SQL笔记-检索出ID为Int或Long中不连续的第一个点

发表评论

最新留言

能坚持,总会有不一样的收获!
[***.219.124.196]2024年05月03日 01时04分33秒