1

I have server-client Qt application, where client sends data packets to server and server reads them at a set time intervals. It happens that client sends data faster than server can read thus filling all the memory on the server side. I am using QAbstractSocket::setReadBufferSize(size) to set max read buffer size on the server side and when it fills up, socket data transferring stops, and data is buffered on client side, which is what i want, but the problem is when server's QTcpSocket's internal read buffer frees up (is not full anymore), data transfer between client and server does not resume.

I've tried to use QAbstractSocket::resume() which seems to work, but Qt5.10 documentation says:

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.

I feel like I should not use that function in this situation, but is there any other solution? How do i know if socket is paused? Why data transfer does not continue automaticaly when QTcpSocket's internal read buffer is not full anymore?

EDIT 1 :

I have downloaded Qt(5.10.0) sources and pdb's to debug this situation and I can see that QAbstractSocket::readData() internal function have line "d->socketEngine->setReadNotificationEnabled(true)" which re-enables data transfering, but QAbstractSocket::readData() gets called only when QTcpSocket internal read buffer is empty (qiodevice.cpp; QIODevicePrivate::read(); line 1176) and in My situation it is never empty, because I read it only when it has enough data for complete packet.

Shouldn't QAbstractSocket::readData() be called when read buffer is not full anymore and not when it's completely empty? Or maybe i do something wrong?

4

1 回答 1

0

找到了解决方法!

在 Qt5.10 源代码中,我可以清楚地看到QTcpSpcket内部读取通知在读取缓冲区已满时被禁用(qabstractsocket.cpp; bool QAbstractSocketPrivate::canReadNotification(); 第 697 行)并且要启用读取通知,您需要读取所有缓冲区以进行它为空或使用QAbstractSocket::setReadBufferSize(newSize),它在 newSize 不为 0(无限制)且不等于 oldSize(qabstractsocket.cpp;void QAbstractSocket::setReadBufferSize(qint64 size);第 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 个字节。

于 2018-03-29T10:32:31.293 回答