此篇博文展示了如何创建一个简单的DTLS服务端:

注意:这个DTLS服务端需要和DTLS客户端一起跑,才能看出效果。

服务端实现了DtlsServer类,这个类使用了QUdpSocket,QDtlsClientVerifier,QDtls使用这些类用于和客户端的连接,完成握手及数据加密传输与读取。

  class DtlsServer : public QObject{Q_OBJECTpublic: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)};

构造函数中QUdpSocket::readyRead()信号连接了readyRead()槽,用于获取客户端的数据报,最简单的设置了DTLS的配置:

  DtlsServer::DtlsServer(){connect(&serverSocket, &QAbstractSocket::readyRead, this, &DtlsServer::readyRead);serverConfiguration = QSslConfiguration::defaultDtlsConfiguration();serverConfiguration.setPreSharedKeyIdentityHint("Qt DTLS example server");serverConfiguration.setPeerVerifyMode(QSslSocket::VerifyNone);}

注意:服务端未使用证书,仅仅是依赖共享密钥(PSK)握手。

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

readyRead()槽函数获取客户端数据报:

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

随后获取客户端的IP和端口,服务端发送数据报给客户端:

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

如果是新的客户端,未知地址和端口,客户端会发送ClientHello消息,随后服务端回HelloVerifyRequest这个要先处理下:

      ...if (client == knownClients.end())return handleNewConnection(peerAddress, peerPort, dgram);...

如果是连上的已知的客户端,服务端会进行解密:

      ...if ((*client)->isConnectionEncrypted()) {decryptDatagram(client->get(), dgram);if ((*client)->dtlsError() == QDtlsError::RemoteClosedConnectionError)knownClients.erase(client);return;}...

以及进行握手:

      ...doHandshake(client->get(), dgram);...

handleNewConnect()对客户端进行认证,以及发送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"));...

如果服务端认为新来的客户端可达,那个服务端会创建一个Qtls的配置,然后进行握手:

      ...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()中

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

在握手中,QDtls::pskRequired()信号关联到pskRequired()槽函数中,并且设置了PSK:

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

在握手完成后,就可以进行数据的加密发送和响应了:

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

服务端关闭DTLS服务调用QDtls::shutdown()

  void DtlsServer::shutdown(){for (const auto &connection : qExchange(knownClients, {}))connection->shutdown(&serverSocket);serverSocket.close();}

下面是出现问题或警告时的代码:

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

Qt文档阅读笔记-DTLS server解析相关推荐

  1. Qt文档阅读笔记-DTLS client解析

    此篇博文讲解了DTLS客户端的编写 注意:DTLS客户端需要结合DTLS服务端一起跑才有效果. 这里使用DTLS客户端使用少量的连接可以和一个或多个DTLS服务端进行通信.DtlsAssociatio ...

  2. Qt文档阅读笔记-QSet官方解析及实例

    目录 官方解析 博主栗子 官方解析 QSet类是一个模板类,他是一个哈希表集合. QSet<T>是Qt的一个普通容器类.QSet存储的值是不指明顺序的,QSet对这些值提供了快速检索的功能 ...

  3. Qt文档阅读笔记-QtWebApp官方解析与实例(使用QtWebApp搭建HTTP服务器)

    目录 官方解析 博主例子 官方解析 QtWepApp是一个C++的http服务器,受到了java Servlets的启发,因为是Qt写的,所以有跨平台的支持. QtWebApp包含如下的组成部分:   ...

  4. Qt文档阅读笔记-QSslConfiguration官方解析与实例

    目录 官网解析 博主例子 官网解析 QSslConfiguration用于SSL连接的配置. QSslConfiguration是Qt networking下的一个类,这个类主要用于打开SSL连接,以 ...

  5. Qt文档阅读笔记-QHostInfo官方解析与实例(根据Host获取IP)

    官方解析 QHostInfo提供了一个静态方法获取主机名: QHostInfo中有一个查找机制,可以根据IP找主机名,也可能工具主机名找IP,可以通过调用QHostInfo::lookupHost这个 ...

  6. Qt文档阅读笔记-QTcpServer官方解析与实例(使用QSocket创建简单的HTTP服务器)

    目录 官方解析 博主例子(做一个简单的HTTP服务器) 本例子中HTTP协议关键点 官方解析 QTcpServer类,提供TCP服务的基础: 这个类接受TCP连接,可以指定一个端口,也可以让其自动一个 ...

  7. Qt文档阅读笔记-QWebPage官方解析与实例

    目录 官方解析 博主例子 源码下载地址 官方解析 QWebPage提供一个视图对象和一个web页面: QWebPage提供了web页面的内容,各种设置(是否支持JavaScript等)和连接,它与QW ...

  8. Qt文档阅读笔记-QWebView官方解析与实例

    目录 背景 官方解析 博主例子 背景 最近发现某Qt项目,出现的效果杠杆的,在看某一小功能的时候,发现有个echart的东西,百度了发现,真的是一个新大陆,Qt加web编程,贼吉尔可怕. 在此发现使用 ...

  9. Qt文档阅读笔记-Q_CLASSINFO官方解析与实例

    官方解析 Q_CLASSINFO 这个宏为类提供额外的信息,要想获取这个Q_CLASSINFO这个信息要使用QObject::metaObject().Qt在Active Qt,Qt D-BUS以及Q ...

最新文章

  1. 量子计算机神器,量子计算技术再获神器 科学家开发出新的成像技术
  2. 服务器无法通过系统非页面共享区来进行分配,因为共享区当前是空的解决办法.供参考....
  3. Linux基于expect(tcl)实现shell自动交互
  4. 绝不重新定义继承而来的缺省值参数
  5. Scala入门到精通——第六节:类和对象(一)
  6. 一文理解 K8s 容器网络虚拟化
  7. static在内存层面的作用_C++内存管理笔记
  8. 微信一年“扫码”约占我国GDP 9%,带动近三千万个就业机会
  9. python手机版做小游戏代码大全-12岁的少年教你用Python做小游戏
  10. iOS8 web下载ipa install App via OTA
  11. 浅谈能源管理系统在钢铁企业上的应用
  12. 运动目标检测发展概述
  13. 【数学建模学习④】飞行管理问题
  14. 汉诺塔(hanio)
  15. 计算机桌面有浮层,电脑桌面悬浮窗记事本有吗?有可以悬浮在电脑桌面上的便签软件吗...
  16. Discuz 实战修改手机模板
  17. SP11 FCTRL - Factorial
  18. 前端埋点需求(vue.js)
  19. springboot之整合Elasticsearch实现搜索
  20. 智能运维案例系列 | 袋鼠云日志助力云南某金融机构日志平台建设,实现核心业务系统运维智能化...

热门文章

  1. 【甘道夫】CDH5.2的Maven依赖
  2. 7-31总结----javascript
  3. CloudPaster日志
  4. 超级棒的免费前端学习路线
  5. 有没有一个永久储存数据的地方?
  6. 学习使用的编程及调试技巧
  7. 飞鸽传书为我们提供了方便的聊天工具
  8. 飞鸽传书内网传输好伙伴
  9. 《极品飞车12》官方网站公布发售日
  10. IBM并购网络视频会议商WebDialogs 加入Lotus Sametime