QSslSocket expire lors de l’attente de données (mais pas QTcpSocket)

Voici une paire d’applications client et serveur simples qui tentent de communiquer via QTcpSocket ou QSslSocket et de s’échanger quelques octets de données. Cela semble fonctionner avec TCP, mais lorsque j’essaie d’utiliser SSL, la lecture expire toujours du côté du serveur. S’il vous plaît dites-moi ce que je fais mal.

Voici mon code complet:

Client:

#include  #include  #include  #include  #define dumpvar(x) qDebug()<<#x<<'='<= 0) t.start(timeout_msec); qint64 bytesRead = 0; auto dataPtr = (char*)data; while (bytesRead < len){ auto bytesNeeded = len - bytesRead; auto ret = d.read(dataPtr, bytesNeeded); if (ret == -1){ throw std::runtime_error(d.errorString().toStdString()); } qDebug()<<QString("Attempted to read %1 bytes, got %2 bytes") .arg(bytesNeeded) .arg(ret); if (ret == 0) { qDebug("Calling waitForReadyRead"); bool ret = d.waitForReadyRead(t.remainingTime()); if (!ret){ qDebug()<<"waitForReadyRead failed"; throw std::runtime_error(d.errorString().toStdString()); } } else { bytesRead += ret; dataPtr += ret; } if (t.remainingTime() == 0) throw std::runtime_error("blockingRead timeout"); } } QByteArray readWrapper(QIODevice &d, qint64 len, int timeout_msec = 10000) { QByteArray ret; ret.resize(len); readWrapper(d, ret.data(), len, timeout_msec); return ret; } void writeWrapper(QIODevice &d, void *data, qint64 len) { auto ret = d.write((char*)data, len); if (ret < len){ throw std::runtime_error(QString("error writing data: %1") .arg(d.errorString()) .toStdString()); } } void writeWrapper(QIODevice &d, const QByteArray &data) { writeWrapper(d, (void*)data.data(), data.size()); } using namespace std; void handleConnection(QAbstractSocket& s) { QByteArray data = "test c"; qDebug()<<"writing data"; writeWrapper(s, data); qDebug()<<"reading data"; auto readData = readWrapper(s, 6); dumpvar(readData); s.disconnectFromHost(); s.waitForDisconnected(); } void tcpclient() { QTcpSocket s; s.connectToHost("localhost", 1234); if(!s.waitForConnected(-1)){ qDebug()<<s.errorString(); return; } qDebug()<<"client connected"; handleConnection(s); } void sslclient() { QSslSocket s; auto cert = QSslCertificate::fromPath("/home/piotrek/cert.pem"); Q_ASSERT(!cert.isEmpty()); s.setCaCertificates({cert}); s.ignoreSslErrors({{QSslError::HostNameMismatch, cert[0]}}); s.connectToHostEncrypted("localhost", 1234); if (!s.waitForEncrypted(10000)){ dumpvar(s.errorString()); dumpvar(s.sslErrors()); return; } qDebug()<<"client connected"; handleConnection(s); } int main(int argc, char** argv) { QCoreApplication a(argc, argv); // tcpclient(); sslclient(); } 

Serveur:

 #include  #include  #include  #include  #include  #include  #define dumpvar(x) qDebug()<<#x<<'='<= 0) t.start(timeout_msec); qint64 bytesRead = 0; auto dataPtr = (char*)data; while (bytesRead < len){ auto bytesNeeded = len - bytesRead; auto ret = d.read(dataPtr, bytesNeeded); if (ret == -1){ throw std::runtime_error(d.errorString().toStdString()); } qDebug()<<QString("Attempted to read %1 bytes, got %2 bytes") .arg(bytesNeeded) .arg(ret); if (ret == 0) { qDebug("Calling waitForReadyRead"); bool ret = d.waitForReadyRead(t.remainingTime()); if (!ret){ qDebug()<<"waitForReadyRead failed"; throw std::runtime_error(d.errorString().toStdString()); } } else { bytesRead += ret; dataPtr += ret; } if (t.remainingTime() == 0) throw std::runtime_error("blockingRead timeout"); } } QByteArray readWrapper(QIODevice &d, qint64 len, int timeout_msec = 10000) { QByteArray ret; ret.resize(len); readWrapper(d, ret.data(), len, timeout_msec); return ret; } void writeWrapper(QIODevice &d, void *data, qint64 len) { auto ret = d.write((char*)data, len); if (ret < len){ throw std::runtime_error(QString("error writing data: %1") .arg(d.errorString()) .toStdString()); } } void writeWrapper(QIODevice &d, const QByteArray &data) { writeWrapper(d, (void*)data.data(), data.size()); } void handleConnection(QAbstractSocket* s) { qDebug()<<__FUNCTION__; QByteArray data = "test s"; qDebug()<<"writing data"; writeWrapper(*s, data); qDebug()<disconnectFromHost(); s->waitForDisconnected(); } class SslServer: public QTcpServer { // QTcpServer interface protected: void incomingConnection(qintptr handle) override { QSslSocket s; if (!s.setSocketDescriptor(handle)){ dumpvar(s.errorSsortingng()); return; } s.setLocalCertificate("/home/piotrek/cert.pem"); Q_ASSERT(!s.localCertificate().isNull()); s.setPrivateKey("/home/piotrek/key.pem", QSsl::Rsa, QSsl::Pem, "test"); // last argument is private key decryption password Q_ASSERT(!s.privateKey().isNull()); s.startServerEncryption(); qDebug()<<"waiting for encrypted"; if(!s.waitForEncrypted(10000)){ dumpvar(s.errorString()); dumpvar(s.sslErrors()); return; } qDebug()<nextPendingConnection()); }); s->listen(QHostAddress::Any, 1234); } void sslserver() { auto s = new SslServer; s->listen(QHostAddress::Any, 1234); } int main(int argc, char *argv[]) { QCoreApplication a(argc, argv); // tcpserver(); sslserver(); return a.exec(); } 

Commande utilisée pour générer un certificate auto-signé:

 openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365 

Voici la sortie sur TCP:

Client:

 client connected writing data reading data "Attempted to read 6 bytes, got 0 bytes" Calling waitForReadyRead "Attempted to read 6 bytes, got 6 bytes" readData = "test s" QAbstractSocket::waitForDisconnected() is not allowed in UnconnectedState 

Serveur:

 handleConnection writing data reading data "Attempted to read 6 bytes, got 0 bytes" Calling waitForReadyRead "Attempted to read 6 bytes, got 6 bytes" readData = "test c" 

Et avec SSL:

Client:

 qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method client connected writing data reading data "Attempted to read 6 bytes, got 6 bytes" readData = "test s" 

Serveur:

 qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method waiting for encrypted server encrypted handleConnection writing data reading data "Attempted to read 6 bytes, got 0 bytes" Calling waitForReadyRead waitForReadyRead failed terminate called after throwing an instance of 'std::runtime_error' what(): Network operation timed out 

EDIT: De plus, si j’ignore la valeur de retour de waitForReadyRead (), read () continue de renvoyer 0, bien que j’aie définitivement écrit 6 octets à l’autre extrémité de la connexion.

Comme vous n’utilisez pas les sockets de manière asynchrone avec les signaux et les slots, vous devez sélectionner le côté qui écrit en premier, puis bloque dans waitForBytesWritten , puis bloque en lecture jusqu’à la réception des données. Et l’autre côté fait les mêmes choses dans l’ordre inverse.

Serveur:

 void handleConnection(QAbstractSocket* s) { qDebug()<<__FUNCTION__; QByteArray data = "test s"; qDebug()<<"writing data"; writeWrapper(*s, data); s->waitForBytesWritten(3000); qDebug()<<"reading data"; auto readData = readWrapper(*s, 6); dumpvar(readData); s->disconnectFromHost(); s->waitForDisconnected(); } 

Client:

 void handleConnection(QAbstractSocket& s) { QByteArray data = "test c"; qDebug()<<"reading data"; auto readData = readWrapper(s, 6); dumpvar(readData); qDebug()<<"writing data"; writeWrapper(s, data); s.waitForBytesWritten(3000); s.disconnectFromHost(); s.waitForDisconnected(); } 

Sortie du serveur:

 qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method waiting for encrypted server encrypted handleConnection writing data reading data "Attempted to read 6 bytes, got 0 bytes" Calling waitForReadyRead "Attempted to read 6 bytes, got 6 bytes" readData = "test c" qt.network.ssl: QSslSocket::waitForDisconnected() is not allowed in UnconnectedState 

Sortie client:

 qt.network.ssl: QSslSocket: cannot resolve SSLv2_client_method qt.network.ssl: QSslSocket: cannot resolve SSLv2_server_method client connected reading data "Attempted to read 6 bytes, got 6 bytes" readData = "test s" writing data Press  to close this window...