Qt文档阅读笔记-DTLS server解析
此篇博文展示了如何创建一个简单的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解析相关推荐
- Qt文档阅读笔记-DTLS client解析
此篇博文讲解了DTLS客户端的编写 注意:DTLS客户端需要结合DTLS服务端一起跑才有效果. 这里使用DTLS客户端使用少量的连接可以和一个或多个DTLS服务端进行通信.DtlsAssociatio ...
- Qt文档阅读笔记-QSet官方解析及实例
目录 官方解析 博主栗子 官方解析 QSet类是一个模板类,他是一个哈希表集合. QSet<T>是Qt的一个普通容器类.QSet存储的值是不指明顺序的,QSet对这些值提供了快速检索的功能 ...
- Qt文档阅读笔记-QtWebApp官方解析与实例(使用QtWebApp搭建HTTP服务器)
目录 官方解析 博主例子 官方解析 QtWepApp是一个C++的http服务器,受到了java Servlets的启发,因为是Qt写的,所以有跨平台的支持. QtWebApp包含如下的组成部分: ...
- Qt文档阅读笔记-QSslConfiguration官方解析与实例
目录 官网解析 博主例子 官网解析 QSslConfiguration用于SSL连接的配置. QSslConfiguration是Qt networking下的一个类,这个类主要用于打开SSL连接,以 ...
- Qt文档阅读笔记-QHostInfo官方解析与实例(根据Host获取IP)
官方解析 QHostInfo提供了一个静态方法获取主机名: QHostInfo中有一个查找机制,可以根据IP找主机名,也可能工具主机名找IP,可以通过调用QHostInfo::lookupHost这个 ...
- Qt文档阅读笔记-QTcpServer官方解析与实例(使用QSocket创建简单的HTTP服务器)
目录 官方解析 博主例子(做一个简单的HTTP服务器) 本例子中HTTP协议关键点 官方解析 QTcpServer类,提供TCP服务的基础: 这个类接受TCP连接,可以指定一个端口,也可以让其自动一个 ...
- Qt文档阅读笔记-QWebPage官方解析与实例
目录 官方解析 博主例子 源码下载地址 官方解析 QWebPage提供一个视图对象和一个web页面: QWebPage提供了web页面的内容,各种设置(是否支持JavaScript等)和连接,它与QW ...
- Qt文档阅读笔记-QWebView官方解析与实例
目录 背景 官方解析 博主例子 背景 最近发现某Qt项目,出现的效果杠杆的,在看某一小功能的时候,发现有个echart的东西,百度了发现,真的是一个新大陆,Qt加web编程,贼吉尔可怕. 在此发现使用 ...
- Qt文档阅读笔记-Q_CLASSINFO官方解析与实例
官方解析 Q_CLASSINFO 这个宏为类提供额外的信息,要想获取这个Q_CLASSINFO这个信息要使用QObject::metaObject().Qt在Active Qt,Qt D-BUS以及Q ...
最新文章
- 量子计算机神器,量子计算技术再获神器 科学家开发出新的成像技术
- 服务器无法通过系统非页面共享区来进行分配,因为共享区当前是空的解决办法.供参考....
- Linux基于expect(tcl)实现shell自动交互
- 绝不重新定义继承而来的缺省值参数
- Scala入门到精通——第六节:类和对象(一)
- 一文理解 K8s 容器网络虚拟化
- static在内存层面的作用_C++内存管理笔记
- 微信一年“扫码”约占我国GDP 9%,带动近三千万个就业机会
- python手机版做小游戏代码大全-12岁的少年教你用Python做小游戏
- iOS8 web下载ipa install App via OTA
- 浅谈能源管理系统在钢铁企业上的应用
- 运动目标检测发展概述
- 【数学建模学习④】飞行管理问题
- 汉诺塔(hanio)
- 计算机桌面有浮层,电脑桌面悬浮窗记事本有吗?有可以悬浮在电脑桌面上的便签软件吗...
- Discuz 实战修改手机模板
- SP11 FCTRL - Factorial
- 前端埋点需求(vue.js)
- springboot之整合Elasticsearch实现搜索
- 智能运维案例系列 | 袋鼠云日志助力云南某金融机构日志平台建设,实现核心业务系统运维智能化...