2

我在循环体中有以下代码片段,负责从 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:上述代码片段的完整算法上下文可以在这个 pastebin 链接中找到。

编辑 4:编辑 3 中的函数版本略有不同,但仍然存在相同的问题,在这个较新的 pastebin 链接上

4

3 回答 3

3

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

所以:

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

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

或者

超级谨慎和

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

然后,

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

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

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

于 2013-05-24T08:16:29.543 回答
2

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

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

如果在套接字上没有可读取的内容时不允许您的代码阻塞,并且您不想将其重塑为事件驱动的结构,请为waitForReadyRead's timeout 使用一个较小的值,以便在实践中它的行为就像不阻塞。

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

于 2013-05-20T22:02:58.723 回答
2

我还没有遇到过这个精确的问题(可能是因为我使用的是不同版本的 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。

也许您的问题无关紧要,但我最近在一位同事编写的代码中看到了类似的问题,这就是我修复它的方法。

于 2013-05-20T16:26:16.993 回答