QSslSocket 在等待数据时超时(但 QTcpSocket 不会)
QSslSocket times out when waiting for data (but QTcpSocket does not)
这是一对简单的客户端和服务器应用程序,它们尝试通过QTcpSocket或QSslSocket进行通信,并相互发送几个字节的数据。它似乎可以通过TCP工作,但是当我尝试使用SSL时,服务器端的读取总是超时。请告诉我我做错了什么。
这是我的完整代码:
客户:
#include <QTcpSocket>
#include <QSslSocket>
#include <QTimer>
#include <QApplication>
#define dumpvar(x) qDebug()<<#x<<'='<<x
// reads exactly len bytes from d into data,
// blocks until data is available
// or throws std::runtime_error on error or timeout
// timeout_msec = -1 means no timeout
void readWrapper(QIODevice &d, void *data, qint64 len, int timeout_msec)
{
QTimer t;
t.setSingleShot(true);
// note: inactive timer's remainingTime() returns -1
if (timeout_msec >= 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();
}
服务器:
#include <QTcpSocket>
#include <QSslSocket>
#include <QTimer>
#include <QCoreApplication>
#include <QTcpServer>
#include <QSslKey>
#define dumpvar(x) qDebug()<<#x<<'='<<x
// reads exactly len bytes from d into data,
// blocks until data is available
// or throws std::runtime_error on error or timeout
// timeout_msec = -1 means no timeout
void readWrapper(QIODevice &d, void *data, qint64 len, int timeout_msec)
{
QTimer t;
t.setSingleShot(true);
// note: inactive timer's remainingTime() returns -1
if (timeout_msec >= 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()<<"reading data";
auto readData = readWrapper(*s, 6);
dumpvar(readData);
s->disconnectFromHost();
s->waitForDisconnected();
}
class SslServer: public QTcpServer
{
// QTcpServer interface
protected:
void incomingConnection(qintptr handle) override
{
QSslSocket s;
if (!s.setSocketDescriptor(handle)){
dumpvar(s.errorString());
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()<<"server encrypted";
handleConnection(&s);
}
};
void tcpserver()
{
QTcpServer *s = new QTcpServer;
QObject::connect(s, &QTcpServer::newConnection, [s]{
handleConnection(s->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();
}
用于生成自签名证书的命令:
openssl req -x509 -newkey rsa:4096 -keyout key.pem -out cert.pem -days 365
以下是 TCP 上的输出:
客户:
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
服务器:
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"
使用SSL:
客户:
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"
服务器:
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
编辑:另外,如果我忽略waitForReadyRead((的返回值,read((仍然会一直返回0,即使我肯定在连接的另一端写入了6个字节。
因为您没有将套接字与信号和插槽异步使用,所以您必须选择哪一端先写入并在 waitForBytesWrite 中阻塞,然后在读取中阻塞,直到收到数据。而另一方以相反的顺序做同样的事情。
服务器:
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();
}
客户:
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();
}
服务器输出:
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
客户端输出:
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 <RETURN> to close this window...
相关文章:
- 将成员变量添加到共享库中的类中,不会破坏二进制兼容性吗
- 整数不会重复超过随机数
- 如何在 C 中正确使用 libiconv 使其不会报告"Arg list too long"?
- 'short int'持有的值溢出,但"自动"不会溢出?
- 是否可以在编译时初始化数组,以便在运行时不会花费时间?
- TDateTime格式在C++Builder中不会更改
- 我的项目不会像"undefined reference to `grpc::g_core_codegen_interface'"那样使用未定义的引用错误进行编译
- 在循环C++中指定字符串之后,不会打印该字符串
- 终端不会为C++文件创建.exe文件吗
- SDL 窗口不会弹出
- OpenGL在启用深度测试时不会丢弃我的碎片
- flutter:即使shouldRepaint()返回true,自定义画家也不会重新绘制
- 从服务器传输到客户端的消息不会出现
- C++Brute Force攻击函数不会返回结果
- 学习多线程C++:添加线程不会使执行速度更快,即使它看起来应该
- 在 C++ 中访问数组负索引处的内存不会返回垃圾
- QSslSocket 在等待数据时超时(但 QTcpSocket 不会)
- QTcpSocket客户端如何理解它在队列中?(并且不会进入连接状态)
- C++/Qt:QTcpSocket 读取后不会写入
- 如何确保来自 QTcpSocket 的 readyRead() 信号不会被错过?