qt qguiapplication 给系统发送键盘消息_Qt官方示例DTLS客户端
❝该示例演示如何实现DTLS客户端。❞「注意:这个DTLS客户端示例旨在与DTLS服务器示例一起运行。」 DTLS客户端可以建立到一个或多个DTLS服务器的多个DTLS连接。客户端DTLS连接由DtlsAssociation类实现。此类使用QUdpSocket读写数据报和QDtls进行加密:classDtlsAssociation:publicQObject{Q_OBJ...
❝该示例演示如何实现DTLS客户端。
❞
「注意:这个DTLS客户端示例旨在与DTLS服务器示例一起运行。」
DTLS客户端可以建立到一个或多个DTLS服务器的多个DTLS连接。客户端DTLS连接由DtlsAssociation类实现。此类使用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)
};
构造函数为新的DTLS连接设置最小的TLS配置,并设置服务器的地址和端口:
...
auto configuration = QSslConfiguration::defaultDtlsConfiguration();
configuration.setPeerVerifyMode(QSslSocket::VerifyNone);
crypto.setPeer(address, port);
crypto.setDtlsConfiguration(configuration);
...
所述QDtls::handshakeTimeout()
信号被连接到ihandleTimeout()
槽处理握手阶段中的数据包丢失和重传:
...
connect(&crypto, &QDtls::handshakeTimeout, this, &DtlsAssociation::handshakeTimeout);
...
为了确保仅从服务器接收数据报,我们将UDP套接字连接到服务器:
...
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()
槽提供了握手阶段所需的预共享密钥(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"));
}
注意:为了简洁起见,pskRequired()
的定义被简化了。QSslPreSharedKeyAuthenticator类的文档详细说明了如何正确实现此槽函数。
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("
"));
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));
}
关于更多
在「QtCreator软件」可以找到:
或在以下「Qt安装目录」找到:
C:\Qt\{你的Qt版本}\Examples\{你的Qt版本}\network\secureudpclient
- 「相关链接」
https://doc.qt.io/qt-5/qtnetwork-secureudpclient-example.html
- Qt君公众号回复『「Qt示例」』获取更多内容。
- 公众号2020-09-20期推文《Qt官方示例-DTLS服务器》。
更多推荐
所有评论(0)