QTcpSocket 数据传输在读取缓冲区已满时停止,并且在释放时不会恢复
QTcpSocket data transfer stops when read buffer is full and does not resumes when it frees up
我有服务器-客户端 Qt 应用程序,其中客户端向服务器发送数据包,服务器以设定的时间间隔读取它们。碰巧客户端发送数据的速度比服务器读取数据的速度快,从而填满了服务器端的所有内存。我正在使用 QAbstractSocket::setReadBufferSize(size) 在服务器端设置最大读取缓冲区大小,当它填满时,套接字数据传输停止,数据在客户端缓冲,这是我想要的,但问题是当服务器的 QTcpSocket 的 内部读取缓冲区释放(不再满)时,客户端和服务器之间的数据传输不会恢复。
我试过使用 QAbstractSocket::resume() 这似乎有效,但 Qt5.10 文档说:
Continues data transfer on the socket. This method should only be used
after the socket has been set to pause upon notifications and a
notification has been received. The only notification currently
supported is QSslSocket::sslErrors(). Calling this method if the
socket is not paused results in undefined behavior.
我觉得在这种情况下我不应该使用该功能,但是还有其他解决方案吗?我怎么知道套接字是否暂停?为什么当 QTcpSocket 的 内部读取缓冲区不再满时,数据传输不会自动继续?
编辑 1:
我已经下载了 Qt(5.10.0) 源代码和 pdb 来调试这种情况,我可以看到 QAbstractSocket::readData() 内部函数有一行“d->socketEngine->setReadNotificationEnabled(true)" 重新启用数据传输,但是 QAbstractSocket::readData() 仅在 [=23= 时被调用]QTcpSocket 内部读取缓冲区为空(qiodevice.cpp;QIODevicePrivate::read();第 1176 行)并且在我的情况下它永远不会为空,因为我只有在它有足够的数据时才读取它完整的数据包。
QAbstractSocket::readData() 不应该在读取缓冲区不再满时调用,而不是在它完全为空时调用?或者我做错了什么?
找到解决方法!
在 Qt5.10 源代码中,我可以清楚地看到 QTcpSpcket 内部读取通知被禁用(qabstractsocket.cpp;bool QAbstractSocketPrivate::canReadNotification();第 697 行)当读取缓冲区已满并启用读取通知时,您需要读取所有缓冲区以使其为空或使用 QAbstractSocket::setReadBufferSize(newSize) 当 newSize 不为 0 时内部启用读取通知(无限制)且不等于 oldSize(qabstractsocket.cpp;void QAbstractSocket::setReadBufferSize(qint64 大小);第 2824 行)。
这是一个简短的功能:
QTcpSocket socket;
qint64 readBufferSize; // Current max read buffer size.
bool flag = false; // flag for changing max read buffer size.
bool isReadBufferLimitReached = false;
void App::CheckReadBufferLimitReached()
{
if (readBufferSize <= socket.bytesAvailable())
isReadBufferLimitReached = true;
else if (isReadBufferLimitReached)
{
if (flag)
{
readBufferSize++;
flag = !flag;
}
else
{
readBufferSize--;
flag = !flag;
}
socket.setReadBufferSize(readBufferSize);
isReadBufferLimitReached = false;
}
}
在以设定的时间间隔从QTcpSocket读取数据的函数中,在读取数据之前,我调用了这个函数,它检查读取缓冲区是否已满并设置isReadBufferLimitReached 如果为真。然后我从 QTcpSocket 读取所需的数据量,最后我再次调用该函数,如果之前缓冲区已满,则调用 QAbstractSocket::setReadBufferSize(size) 来设置新缓冲区大小并启用内部读取通知。将读取缓冲区大小更改 +/-1 应该是安全的,因为您至少从套接字读取了 1 个字节。
我有服务器-客户端 Qt 应用程序,其中客户端向服务器发送数据包,服务器以设定的时间间隔读取它们。碰巧客户端发送数据的速度比服务器读取数据的速度快,从而填满了服务器端的所有内存。我正在使用 QAbstractSocket::setReadBufferSize(size) 在服务器端设置最大读取缓冲区大小,当它填满时,套接字数据传输停止,数据在客户端缓冲,这是我想要的,但问题是当服务器的 QTcpSocket 的 内部读取缓冲区释放(不再满)时,客户端和服务器之间的数据传输不会恢复。
我试过使用 QAbstractSocket::resume() 这似乎有效,但 Qt5.10 文档说:
Continues data transfer on the socket. This method should only be used after the socket has been set to pause upon notifications and a notification has been received. The only notification currently supported is QSslSocket::sslErrors(). Calling this method if the socket is not paused results in undefined behavior.
我觉得在这种情况下我不应该使用该功能,但是还有其他解决方案吗?我怎么知道套接字是否暂停?为什么当 QTcpSocket 的 内部读取缓冲区不再满时,数据传输不会自动继续?
编辑 1:
我已经下载了 Qt(5.10.0) 源代码和 pdb 来调试这种情况,我可以看到 QAbstractSocket::readData() 内部函数有一行“d->socketEngine->setReadNotificationEnabled(true)" 重新启用数据传输,但是 QAbstractSocket::readData() 仅在 [=23= 时被调用]QTcpSocket 内部读取缓冲区为空(qiodevice.cpp;QIODevicePrivate::read();第 1176 行)并且在我的情况下它永远不会为空,因为我只有在它有足够的数据时才读取它完整的数据包。
QAbstractSocket::readData() 不应该在读取缓冲区不再满时调用,而不是在它完全为空时调用?或者我做错了什么?
找到解决方法!
在 Qt5.10 源代码中,我可以清楚地看到 QTcpSpcket 内部读取通知被禁用(qabstractsocket.cpp;bool QAbstractSocketPrivate::canReadNotification();第 697 行)当读取缓冲区已满并启用读取通知时,您需要读取所有缓冲区以使其为空或使用 QAbstractSocket::setReadBufferSize(newSize) 当 newSize 不为 0 时内部启用读取通知(无限制)且不等于 oldSize(qabstractsocket.cpp;void QAbstractSocket::setReadBufferSize(qint64 大小);第 2824 行)。 这是一个简短的功能:
QTcpSocket socket;
qint64 readBufferSize; // Current max read buffer size.
bool flag = false; // flag for changing max read buffer size.
bool isReadBufferLimitReached = false;
void App::CheckReadBufferLimitReached()
{
if (readBufferSize <= socket.bytesAvailable())
isReadBufferLimitReached = true;
else if (isReadBufferLimitReached)
{
if (flag)
{
readBufferSize++;
flag = !flag;
}
else
{
readBufferSize--;
flag = !flag;
}
socket.setReadBufferSize(readBufferSize);
isReadBufferLimitReached = false;
}
}
在以设定的时间间隔从QTcpSocket读取数据的函数中,在读取数据之前,我调用了这个函数,它检查读取缓冲区是否已满并设置isReadBufferLimitReached 如果为真。然后我从 QTcpSocket 读取所需的数据量,最后我再次调用该函数,如果之前缓冲区已满,则调用 QAbstractSocket::setReadBufferSize(size) 来设置新缓冲区大小并启用内部读取通知。将读取缓冲区大小更改 +/-1 应该是安全的,因为您至少从套接字读取了 1 个字节。