C++ 如何确保来自QTcpSocket的readyRead()信号能够';你不会错过吧?
使用C++ 如何确保来自QTcpSocket的readyRead()信号能够';你不会错过吧?,c++,qt,qtcpsocket,C++,Qt,Qtcpsocket,使用qtcsocket接收数据时,要使用的信号是readyRead(),表示新数据可用。 但是,当您在相应的插槽实现中读取数据时,不会发出额外的readyRead()。 这可能是有意义的,因为您已经在函数中读取所有可用数据 问题描述 但是,假设此插槽的以下实现: void readSocketData() { datacounter += socket->readAll().length(); qDebug() << datacounter; } 然而,我们还
qtcsocket
接收数据时,要使用的信号是readyRead()
,表示新数据可用。
但是,当您在相应的插槽实现中读取数据时,不会发出额外的readyRead()
。
这可能是有意义的,因为您已经在函数中读取所有可用数据
问题描述
但是,假设此插槽的以下实现:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
然而,我们还没有解决这个问题。数据仍然可能在socket->bytesavable()
-check之后到达(甚至在函数的绝对结尾处进行另一次检查也不能解决这个问题)
确保能够重现问题
由于这个问题当然很少发生,我坚持使用插槽的第一个实现,我甚至会添加一个人工超时,以确保问题发生:
void readSocketData()
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
// wait, to make sure that some data arrived
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
}
由于我不直接调用插槽,而是使用QTimer::singleShot()
,因此我假设qtcsocket
无法知道我再次调用插槽,因此不再发出readyRead()
的问题
我之所以包含参数bool selfCall
,是因为不允许qtcsocket
调用的插槽提前退出,否则同样的问题可能再次发生,即数据恰好在错误的时刻到达,并且不会发出readyRead()
这真的是解决我问题的最好办法吗?
这个问题的存在是Qt中的设计错误还是我遗漏了什么?以下是一些获取整个文件的方法示例,但使用QNetwork API的其他部分: 这些示例展示了一种更强大的方法来处理TCP数据,以及当缓冲区已满时,使用更高级别的api更好地处理错误 如果您仍然想使用较低级别的api,下面是一篇文章,其中介绍了一种处理缓冲区的好方法: 在
readSocketData()中执行以下操作:
void readSocketData()
{
while(socket->bytesAvailable())
datacounter += socket->readAll().length();
qDebug() << datacounter;
}
if (bytesAvailable() < 256)
return;
QByteArray data = read(256);
if(字节可用()<256)
返回;
QByteArray数据=读取(256);
编辑:有关如何与QTCPSocket交互的其他示例:
希望有帮助。这个问题很有趣
在我的程序中,QTcpSocket的使用非常密集。所以我编写了整个库,它将传出的数据分解成带有标题、数据标识符、包索引号和最大大小的包,当下一个数据块出现时,我确切地知道它属于哪里。即使我错过了什么,当下一个readyRead
到来时,接收器也会读取所有数据并正确地合成接收到的数据。如果您的程序之间的通信不是很激烈,您也可以这样做,但是使用计时器(虽然不是很快,但可以解决问题)
关于你的解决方案。我不认为这比这个更好:
void readSocketData()
{
while(socket->bytesAvailable())
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
}
}
void readSocketData()
{
while(套接字->字节可用()
{
datacounter+=socket->readAll().length();
qDebug()我认为本主题中提到的场景有两种主要情况,它们的工作方式不同,但一般来说,QT根本没有这个问题,我将尝试在下面解释原因
第一种情况:单线程应用程序
Qt使用select()系统调用轮询打开的文件描述符是否有任何更改或操作可用。在每个循环上简单地说,Qt检查打开的文件描述符是否有数据可供读取/关闭等。因此,单线程应用程序流看起来是这样的(代码部分简化)
所以,如果新数据到达时程序仍然在slotradeyread中,会发生什么呢?老实说,没有什么特别的。操作系统将缓冲数据,一旦控制返回到下一个执行,select()操作系统将通知软件有数据可用于特定的文件句柄。它的工作方式与TCP套接字/文件等完全相同
我可以想象这样的情况(如果慢速读取延迟很长,并且有大量数据进入),您可能会在操作系统FIFO缓冲区(例如串行端口)中遇到超限,但这更多地与糟糕的软件设计有关,而不是与QT或操作系统问题有关
您应该在中断处理程序上查看readyRead之类的插槽,并将其逻辑仅保留在fetch功能中,该功能将填充您的内部缓冲区,而处理应在单独的线程中完成,或者应用程序处于空闲状态等。原因是,任何此类应用程序通常都是一个大规模服务系统,如果它在为一个请求提供服务,那么两个请求之间的时间间隔它的队列无论如何都会溢出
第二种场景:多线程应用程序
事实上,这个场景与1)期望您应该正确设计在每个线程中发生的事情没有太大区别。如果您使用轻量级的“伪中断处理程序”保持主循环,您将完全可以在其他线程中保持处理逻辑,但此逻辑应该使用您自己的预取缓冲区,而不是QIODevice。简短回答
QIODevice::readyRead()的
readyRead()
不是递归发出的;如果在连接到readyRead()
信号的插槽内重新进入事件循环或调用waitForReadyRead()
,则不会重新发出该信号
因此,请确保
- 不要在插槽中实例化
QEventLoop
- 不要在插槽内调用
QApplication::processEvents()
- 不要在插槽中调用
QIODevice::waitForReadyRead()
- 不要在不同线程中使用相同的
QTcpSocket
实例
现在您应该始终收到所有<
void readSocketData()
{
while(socket->bytesAvailable())
{
datacounter += socket->readAll().length();
qDebug() << datacounter;
QEventLoop loop;
QTimer::singleShot(1000, &loop, SLOT(quit()));
loop.exec();
}
}
int mainLoop(...) {
select(...);
foreach( descriptor which has new data available ) {
find appropriate handler
emit readyRead;
}
}
void slotReadyRead() {
some code;
}
// Only emit readyRead() when not recursing.
if (!emittedReadyRead && channel == currentReadChannel) {
QScopedValueRollback<bool> r(emittedReadyRead);
emittedReadyRead = true;
emit q->readyRead();
}
void slot_readSocketData() {
while (m_pSocket->bytesAvailable()) {
m_sReceived.append(m_pSocket->readAll());
m_pProgessDialog->setValue(++m_iCnt);
}//while
}//slot_readSocketData
void MyFunExample::readyRead()
{
bool done = false;
while (!done)
{
in_.startTransaction();
DataLinkListStruct st;
in_ >> st;
if (!in_.commitTransaction())
qDebug() << "Failed to commit transaction.";
switch (st.type)
{
case DataLinkXmitType::Matrix:
for ( int i=0;i<st.numLists;++i)
{
for ( auto it=st.data[i].begin();it!=st.data[i].end();++it )
{
qDebug() << (*it).toString();
}
}
break;
case DataLinkXmitType::SingleValue:
qDebug() << st.value.toString();
break;
case DataLinkXmitType::Map:
for (auto it=st.mapData.begin();it!=st.mapData.end();++it)
{
qDebug() << it.key() << " == " << it.value().toString();
}
break;
}
if ( client_->QIODevice::bytesAvailable() < sizeof(DataLinkListStruct) )
done = true;
}
}