我正在开发一个使用SSL套接字进行通信的应用程序,当一个不受信任的客户端连接到服务器时,它会发出sslError现在我应该要求用户使用QDialog::exec()做出决定(我不能进行CNOOC操作,因为我需要处理sslError内部的错误,无论是删除SslError还是中止连接都取决于用户的决定)但是当我打开对话框时,客户端断开连接(客户端套接字似乎被删除),然后QDialog exec返回当我访问套接字时,它会抛出一个错误,因为内存访问违规。
Qt docs for QDialog::exec说:
注意:避免使用此功能;相反,使用open()。与exec()不同,open()是异步的,并且不会旋转额外的事件循环。这可以防止一系列危险的错误发生(例如,在对话框打开时通过exec()删除对话框的父对话框)。当使用open()时,您可以连接到QDialog的finished()信号,以便在对话框关闭时得到通知。
在这种情况下我该怎么办?
我有一个身份验证的替代过程,如果有任何sslHandshakeErrors,那么稍后我将异步显示对话框,如果用户按ok,我发送一个名为authOk的数据包,然后继续读取,否则我发送一个名为authFailed的数据包,并断开客户端。但在这个过程中,似乎没有使用标准的SSL连接握手。
有更好的解决方案,谢谢:)
编辑:
范例:
#include <QApplication>
#include <QSslServer>
#include <QDialog>
#include <QSslKey>
#include <QMessageBox>
int main(int argc, char **argv) {
// create an Application
QApplication app(argc, argv);
// set the ssl configuration
auto config = QSslConfiguration();
// set certificate
config.setLocalCertificate(QSslCertificate::fromData(QByteArray::fromStdString(R"(
-----BEGIN CERTIFICATE-----
MIIDITCCAgmgAwIBAgIVAMdt4c6oGd0rUSbR+/tBVfhny3K3MA0GCSqGSIb3DQEB
BQUAMEoxGDAWBgNVBAMMD0xBUFRPUC1KQzJNMzcyQTEbMBkGA1UECgwSc3JpbGFr
c2htaWthbnRoYW5wMREwDwYDVQQLDAhjbGlwYmlyZDAeFw0yMzA5MDgxMTQ4NDZa
Fw0yNDA5MDcxMTQ4NDZaMEoxGDAWBgNVBAMMD0xBUFRPUC1KQzJNMzcyQTEbMBkG
A1UECgwSc3JpbGFrc2htaWthbnRoYW5wMREwDwYDVQQLDAhjbGlwYmlyZDCCASIw
DQYJKoZIhvcNAQEBBQADggEPADCCAQoCggEBALkFDUKmzz+NE0x7o5JGgE8KZ/cB
GEtTwN2cElMESQ/MZe3ohIE5GdFkld/7894wXZAJkXu38oZVEQ/AurntqqSHT4iP
7qMUJGYjo2v2g/1JwU3E/sG+IjatOjyl8b0b+E9TmDt7XOS4VWz3vjTkXwmQTzUv
3L5myfdcmBpA9AOXxH8yHq9lcBl97ZHGWA4zI9uWnwtyFWSl8DX4H/y14+itEYwH
n6xLsEBxqjx40G7WR0AiAYRdE5Yvr6QOGtFAeODFqOv/2sAxdm/7P1wHjIvyOB++
eufvPyWZpzeBfEvrIeGkj91YGAV7FQsOfCuzetGgZcLiBDAZKjnKEQ2oajUCAwEA
ATANBgkqhkiG9w0BAQUFAAOCAQEAD/SYccAb3K6GKyfc9Rbaj44IxpsNlHDQAr5b
c8Nmz+LW905EqFe6tAhCgi3q9o3HUUeiNHe0rYad3Lgd1setSOVdWiSbxArmELgW
Dg3NGd3GIIRShvmZfSHRkpvKaD9j06CVzMrM0nZsjQVcQrKlFBUJ9UEqVmmcz1nU
a4yUEQ9Rb7t4Icw7aD07NqLRlhNGCii4d12NAY7kRZdLdtTw7T/j4tXxmcJsOiWK
Lx/cqqTBgBHc3l3EeylQdO17pFClY5yMUGGla7LPcDm1sU4mmFEKmkjcRGu+mQM2
V3EDgh37GyTWvs6Zf86B14m/US0Ff4vQu26vco5Pjk3xKckA+Q==
-----END CERTIFICATE-----
)")).first());
// set private key
config.setPrivateKey(QSslKey(QByteArray::fromStdString(R"(
-----BEGIN RSA PRIVATE KEY-----
MIIEpAIBAAKCAQEAuQUNQqbPP40TTHujkkaATwpn9wEYS1PA3ZwSUwRJD8xl7eiE
gTkZ0WSV3/vz3jBdkAmRe7fyhlURD8C6ue2qpIdPiI/uoxQkZiOja/aD/UnBTcT+
wb4iNq06PKXxvRv4T1OYO3tc5LhVbPe+NORfCZBPNS/cvmbJ91yYGkD0A5fEfzIe
r2VwGX3tkcZYDjMj25afC3IVZKXwNfgf/LXj6K0RjAefrEuwQHGqPHjQbtZHQCIB
hF0Tli+vpA4a0UB44MWo6//awDF2b/s/XAeMi/I4H7565+8/JZmnN4F8S+sh4aSP
3VgYBXsVCw58K7N60aBlwuIEMBkqOcoRDahqNQIDAQABAoIBADf6I0zrEwDzOceG
ELMMyX0gdNvyZNtFd7CUq6aaQVCaUqxsEMrX78u+VunxXJL4pxYRDwcTXDjwO708
XkIqedpVZea3RUfprCmK1sKvTrevPOC+nSUY1Vkdh+UZf83rTHETpZc5d51rd80E
F3QBNA+8rXo2BN9GUgyY4xvuUuVS3drVZ6WA9A9yPtaBgeLATqmb0Ckh2aVn+XG9
eYIxiF0Sfnb3HET7IDO+Xtw8OIygq+dT9v5LMMYf/Aa8aakJMoTK6SLbjFWs5gUj
mhBSBdxqdmLLTa2E+3hrZOfgQ0tk85336n7v3dFKkhTMr9KztOIh53Ruh36gR3UT
QxbCyvECgYEA2uKcFmrkGa61WarqSbB58e/m111cmlcjOSoEELwb+O1pLe8qqDSZ
UeLVMRW+omdyvt0N1RaDEOsBWtgup2RntutEx8sAi5O1d+CrCLZ7xZJPSrYH4tIs
YFXoBgCNqn+275ZdeL5LgxuFKAstFW9YbWpsXCEPbrIMBk4Xm3m3Pt8CgYEA2GRo
I+lLPvcslTwens+5sXT5+EPTcY9Ss2jSz3njEIt4asxa/P4H7Xx2EWqMQg5LGSVD
MP8L9lxCSiyYvrQhYAQZq2VqvVCejFkrWY8hntDvyhJ26SfanSdMB1MVWOc16dns
wjuuX3+5QIQoMogL1eng6/VOOJHVfiAh57yqPWsCgYEAkcI54wvHXfrjtRSF9BBb
BGuXM29ujTDdueFq16IMlpWyZu5PX7e3Kbp98bPjQM7WsJcP8QiOuyNjwZUYbEwG
bN767HkYodn5DB1GiATNI2Is/zl8wuTmvDg4zFZuAE4QCjf9grxmGKao42Od4BpH
roUiJ6+0USirrT8vpU9GYc0CgYAYuIfJKnrFK7m1JtQcsoB1THbOLPl37La29k+3
EialmjlcghIW+vJu6BwY60Iwva9IpSAi9dApCesszCF7D9sMPAuur/xculwSjpFM
PvTJTvdF74wUINBxya5+27gBmxBmsdXBbs4B7PZ971skQrSPcJOYgUK5ZbetHACj
l8MfFwKBgQC3g39rKU/iahrr5VcbLecsdv9jhSo+PANQOXhreEZTOF1spsWh9bMN
11MB0uZu99p4Solvv0M11Md++CO3ocBRT0AsdsdLdytAb+YWM+c1ls5dhjVnaMpL
dKKHiQ7sfiPM05f6HuTmMuinSLw7f1Ff7GVJUMugJCqiNn3XO3jWGQ==
-----END RSA PRIVATE KEY-----
)"), QSsl::KeyAlgorithm::Rsa));
// peer verify
config.setPeerVerifyMode(QSslSocket::VerifyPeer);
// if any of them is null
if (config.isNull() || config.localCertificate().isNull() || config.privateKey().isNull()) {
throw std::runtime_error("Can't Create QSslConfiguration");
}
const auto processSslErrors = [=] (QSslSocket * sock, const QList<QSslError> & errors){
// create a dialog
auto dialog = QMessageBox(QMessageBox::Critical, "SSL Error", "Accept ?", QMessageBox::Yes | QMessageBox::Cancel);
// show the dialog
dialog.exec();
// if user accepted
if (dialog.result() == QMessageBox::Yes) {
sock->ignoreSslErrors();
}
};
// create the server
auto server = new QSslServer();
// set the ssl errors handler
QObject::connect(server, &QSslServer::sslErrors, processSslErrors);
// set the ssl configuration
server->setSslConfiguration(config);
// listen to the port
server->listen(QHostAddress::Any, 7000);
// exec
return app.exec();
}
例外情况:
在客户端:
openssl s_client -connect 127.0.0.1:7000
当对话框处于打开状态时,您可以看到客户端已断开连接
2条答案
按热度按时间0pizxfdo1#
一个更好的解决方案可能是异步接受ssl错误:
即使Qt支持同步方法,同步请求用户同意也经常会导致连接超时,因为用户可能响应不够快。
一个基本的代码示例(在真实的情况下,您应该检查报告的错误):
eyh26e7m2#
这是建筑学的问题。
QDialog::exec
创建一个模式对话框,它会停止主线程内对话框之外的大多数事件处理,但不会停止其他线程。你需要有一个或两个额外的线程,所以你可以有单独的偶数循环:
1.管理器线程(MT)-状态机,跟踪应用程序的状态(可选)
1.通信线程(CT)-会话或套接字所在的位置
MT的状态机可能在Communication中实现,但请确保在需要的地方使用queued connection。
这样,你就可以完全消除每一边可能的各种变化状态。用户的动作将被发送给MT,MT将决定如何处理CT状态并将当前的开发通知UI。CT处理数据,当结果准备好时,它向MT或UI发出信号。
作为一个附带的好处,你会有一个结构匹配单一责任原则和可能的开放封闭原则。你只会改变UI来改变它对用户操作或状态变化的React,即。“输入”。你改变CT只是为了改变方案。
请注意,您只能将没有父对象的对象移动到另一个线程,否则
moveToThread
将默默失败。为了简化UI方面的事情,你可能需要某种构建器模式,一个你移动到线程的对象,它将创建其余的东西。