【问题标题】:Why can't my code read the number of bytes available on a QTcpSocket? Ideas?为什么我的代码无法读取 QTcpSocket 上可用的字节数?想法?
【发布时间】:2013-05-15 05:21:45
【问题描述】:

我在循环体中有以下 sn-p 代码,负责从 QTcpSocket 读取数据(nntp 是指向 QTcpSocket 的指针)。

std::vector<char> buffer;
int bytesAvailable = nntp->bytesAvailable();
qDebug() << "bytesAvailable: "<<bytesAvailable;
if(bytesAvailable <= 0) break;
buffer.resize(bytesAvailable);
bytesRead = nntp->read(&buffer[0], bytesAvailable);
qDebug() << (nntp->state() == QAbstractSocket::ConnectedState);
qDebug() << "bytesRead: "<<bytesRead;

这会间歇性地输出类似于以下内容的内容:

bytesAvailable:  24 
true 
bytesRead:  0 

我的代码从那里开始出现错误行为。这对我来说似乎很奇怪,并且表明我完全误解了 QTcpSockets 的工作原理。当然,如果 bytesAvailable > 0 那么后续读取将意味着可以将 bytesAvailable 字节读入缓冲区,在这种情况下 bytesRead 应该 == bytesAvailable。还是我错过了什么?我目前的怀疑是它可能是某种内存损坏..

编辑:抛出一些 nntp.errorString() 消息报告在此失败期间,“网络操作超时”。我需要调查这意味着什么......(想法?)

编辑2:似乎“网络操作超时”只是意味着读取超时。当代码正常工作时(即间歇性地),我仍然会收到这个“错误”。

编辑 3:上述 sn-p 代码的完整算法上下文可以在 at this pastebin link 找到。

编辑 4:编辑 3 中的函数版本略有不同,但仍然存在相同的问题,位于 this newer pastebin link

【问题讨论】:

  • 我真的不喜欢这种结构:&amp;buffer[0]。相反,请尝试buffer = nntp-&gt;readAll();
  • 感谢您的建议 Amartel 但切换到 QByteArray 然后执行 readAll 然后使用 QByteArray 的大小来确定读取的字节数仍然会导致相同的问题

标签: c++ qt sockets qtcpsocket


【解决方案1】:

我发现了问题:我一直试图从中读取的 QTcpSocket 属于不同的线程。即使根据 QIODevice 的缓冲区可以使用字节,也因此无法读取它们。

因此:

* 始终确保您要读取的套接字属于您要读取的线程 *

为了解决这个问题,正如 Taylor 上面建议的那样,可以利用 QTcpSocket 的信号和插槽基础设施(首选)。这具有适当的线程基础设施,理论上应该使事情变得更简单。

要非常谨慎和

(a)利用自己的QThread,将包含QTcpSocket的对象移动到这个QThread

那么,

(b) 在这个其他线程的阻塞读取循环中使用 QTcpSocket 的 waitForReadyRead。

后一种方法比较困难,因为如果想在以后的读写中为其他线程保留 QTcpSocket,那么在它被移动到另一个线程并由另一个线程处理之后,它必须移回主线程线程之前,它可以被移动到另一个线程。 —— 只是想把它说出来就让我头疼!当然,可以选择始终保持相同的 QTcpSocket 工作线程,以便只移动一次,或者每次需要 QTcpSocket 时简单地创建一个新的 QTcpSocket,将其移动到自己的 QThread,然后在完成后立即删除它与。

基本上,如果可以,请使用第一个信号和插槽方法。

【讨论】:

    【解决方案2】:

    如果 bytesAvailable() 报告在 QIODevice 内部缓冲区中等待的数据大小加上操作系统报告的大小(例如在 Linux 中将由 ioctl(fd,FIONREAD,&amp;bytesCount) 获得),可能会发生什么。

    这本身是有道理的,但是为了能够在没有事件循环的情况下从QTcpSocket 读取这些字节,必须在您自己的循环中调用waitForReadyRead()。否则内核缓冲区中的数据不会进入QIODevice 的缓冲区。

    如果在套接字上没有可读取的内容时不允许您的代码阻塞,并且您不想将其重塑为事件驱动的结构,请为waitForReadyRead 的超时使用一个较小的值,以便在练习它会表现得好像没有阻塞一样。

    如果您也在写入没有事件循环的套接字,也不要忘记调用waitForBytesWritten()

    【讨论】:

    • 谢谢丹尼尔,这是有道理的。实际上,我确实在完整循环的上下文中使用了 waitForReadyRead。有关链接,请参见原始帖子中的 EDIT 3。
    • 我会把它从外循环移到阅读bytesAvailable()之前,看看这是否有区别,特别是因为外循环是一个do..while而不是while..做
    • 谢谢丹尼尔,我已经尝试过你的建议(参见 EDIT 4 链接中的代码),但我仍然遇到同样的问题
    • 我的理论就这么多。好吧,如果 waitForReadyRead() 返回 true,然后 read() 读取 0 字节,那么我看不出这是怎么发生的,除非同时有其他东西读取内容。
    • 说实话,我真的很困惑。没有其他东西可以读取内容。也许我使用线程的方式搞砸了..
    【解决方案3】:

    我还没有遇到过这个精确的问题(可能是因为我使用的是不同版本的 Qt),但我建议尝试从循环切换到事件驱动的方法。如果您的循环在主线程中运行,则对象无法传递排队信号(因为 QTcpSocket 类可能在内部执行),这可能就是您看到“网络操作超时”的原因?

    所以连接一些基本的 QTcpSocket 信号:

    connect(nntp, SIGNAL(disconnected()),
            this, SLOT(onDisconnected()));
    connect(nntp, SIGNAL(readyRead()),
            this, SLOT(onReadyRead()));
    connect(nntp, SIGNAL(error(QAbstractSocket::SocketError)),
            this, SLOT(onSocketError(QAbstractSocket::SocketError)));
    

    然后将您现有的“读取”代码放入 onReadyRead。

    也许你的问题是无关的,但我最近在同事写的代码中看到了类似的问题,这就是我解决它的方法。

    【讨论】:

    • 感谢 Taylor -- 我也更喜欢信号和插槽的处理方式,将来我可能会改用这种方法。我选择循环的原因是因为我需要能够读取一组格式化数据,这些数据根据几个转义序列进行标记;在循环中,根据这些标记解析数据相对容易,而且在我知道它应该可用的确切时刻。话虽如此,我的方法显然有问题,所以我最终可能需要像你推荐的那样切换到信号和插槽..
    猜你喜欢
    • 2020-02-16
    • 1970-01-01
    • 2021-03-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多