QSslSocket等待数据时超时(但QTcpSocket不会)

2023-12-31

这是一对简单的客户端和服务器应用程序,它们尝试通过 QTcpSocket 或 QSslSocket 进行通信并相互发送几个字节的数据。它似乎可以通过 TCP 工作,但是当我尝试使用 SSL 时,读取总是在服务器端超时。请告诉我我做错了什么。

这是我的完整代码:

Client:

#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();

}

Server:

#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:

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

Server:

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:

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"

Server:

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 个字节。


因为您没有将套接字与信号和插槽异步使用,所以您必须选择哪一侧先写入,然后blocks in 等待字节写入 http://doc.qt.io/qt-5/qiodevice.html#waitForBytesWritten然后阻塞读取直到收到数据。另一方以相反的顺序做同样的事情。

Server:

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();
}

服务器输出:

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...
本文内容由网友自发贡献,版权归原作者所有,本站不承担相应法律责任。如您发现有涉嫌抄袭侵权的内容,请联系:hwhale#tublm.com(使用前将#替换为@)

QSslSocket等待数据时超时(但QTcpSocket不会) 的相关文章

  • SetWindowsHookEx 函数返回 NULL

    我正在研究 DLL 注入 但收到错误如下 挂接进程失败 87 参数不正确 目标进程和dll都是64位的 注入代码为 BOOL HookInjection TCHAR target TCHAR dll name https msdn micr
  • 检查列表是否包含另一个列表。 C#

    编辑 只是说 ContainsAllItem 中的注释解释得最好 很抱歉问这个问题 我知道以前有人问过这个问题 但我只是不明白 好的 所以我想检查一个列表是否包含另一个列表中的所有项目WITHOUT重叠 以及根据类字符串 名称变量 称为项目
  • WebClient读取错误页面的内容

    我有一个加载页面内容的应用程序 我使用 WebClient 类 即使服务器返回 404 500 等错误 我也需要检索内容 我需要这样的东西 WebClient wc new WebClient string pageContent try
  • Android NDK C++“wstring”支持

    我有用 C 编写的源代码 lib 现在我想在 Android NDK 项目 NDK 6 中编译并使用相同的源代码 lib 我能够编译大多数 C 文件 除了基于 std wstring 的功能 在 Application mk 中 当我指定时
  • 为什么派生类不使用基类的operator=(赋值运算符)?

    以下是实际问题的简化版本 而不是打电话Base operator int 代码似乎生成了一个临时的Derived对象并复制它 既然函数签名似乎完美匹配 为什么不使用基本赋值运算符 这个简化的示例没有显示任何不良影响 但原始代码在析构函数中有
  • 通过单个 GPIO 引脚转储闪存

    我正在使用 Infineon 的 XMC4500 Relax Kit 并尝试通过单个 GPIO 引脚提取固件 我非常天真的想法是通过 GPIO 引脚一次转储一位 然后用逻辑分析仪以某种方式 嗅探 数据 伪代码 while word by w
  • 在运行时设置 DataGridView 上的 DataFormatString?

    是否可以在运行时设置 ASP NET DataGridView 中的列或单元格的 DataFormatString 属性 这应该有效 BoundField priceField grid Columns 0 as BoundField pr
  • 通过引用传递时取消引用指针

    当通过引用传递给函数时取消引用指针时会发生什么 这是一个简单的例子 int returnSame int example return example int main int inum 3 int pinum inum std cout
  • 将 C# 反射代码移植到 Metro-Ui

    我正在尝试移植使用反射的现有 C 类 通用工厂 但我无法编译这段代码 Type types Assembly GetAssembly typeof TProduct GetTypes foreach Type type in types i
  • F10键没被抓住

    I have a Windows Form and there overriden ProcessCmdKey However this works with all of the F Keys except for F10 I am tr
  • 特定设备的不同字体大小

    我目前正在开发通用应用程序 我需要分别处理移动设备和桌面的文本框字体大小 我找到了一些方法 但都不能解决问题 使用 VisualStateManager 和 StateTrigger 为例
  • 使用scanf()时如何区分整数和字符

    我只是使用该功能scanf 代码如下 scanf d a printf d a 当我输入1时 它会像我想要的那样打印1 但即使我输入 1a 它也会像以前一样打印 1 当用户输入非整数时 例如 2 3 12ab 1 a 我想向用户显示 输入整
  • 更改其他页面的主窗口内容

    在 WPF 应用程序的主窗口中 我有一个 Badged 元素 来自材料设计 这是我的代码
  • C# 中处理 SQL 死锁的模式?

    我正在用 C 编写一个访问 SQL Server 2005 数据库的应用程序 该应用程序是数据库密集型的 即使我尝试优化所有访问 设置适当的索引等 我预计迟早会遇到死锁 我知道为什么会发生数据库死锁 但我怀疑我能否在某个时候发布不发生死锁的
  • 为什么WCF中不允许方法重载?

    假设这是一个ServiceContract ServiceContract public interface MyService OperationContract int Sum int x int y OperationContract
  • 当在 Repository/UnitOrWork 之上使用 Service 类时,我应该在哪里放置逻辑不适合 Repository 的常用数据访问代码?

    In my 先前的问题 https stackoverflow com questions 24906548 using the generic repository unit of work pattern in large projec
  • 在 .NET 中记录 StackOverflowException

    最近 我的 NET 应用程序 asp net 网站 中出现了堆栈溢出异常 我之所以知道该异常是因为它出现在我的 EventLog 中 我知道 StackOverflow 异常无法被捕获或处理 但是有没有办法在它杀死您的应用程序之前记录它 我
  • Dynamics Crm:获取状态代码/状态代码映射的元数据

    在 Dynamics CRM 2011 中 在事件实体上 状态原因 选项集 也称为状态代码 与 状态 选项集 也称为状态代码 相关 例如看这个截图 当我使用 API 检索状态原因选项集时 如下所示 RetrieveAttributeRequ
  • 微软语音识别速度

    我正在使用微软的语音识别器开发一个小型练习应用程序 对于我正在做的事情来说 我似乎无法让它足够快地识别单个单词 我希望能够正常说话 系统将从我所说的内容中抓取 关键字 并生成一个字符串 目前我正在使用 5 个单词的自定义语法 红 蓝 黄 绿
  • 是否有任何不使用公共虚拟方法的正当理由? [关闭]

    Closed 这个问题是基于意见的 help closed questions 目前不接受答案 是否有任何不使用公共虚拟方法的正当理由 我在某处读到我们应该避免使用公共虚拟方法 但我想向专家确认这是否是有效的声明 对于良好且稳定的 API

随机推荐