Передача данных QTcpSocket останавливается, когда буфер чтения заполнен, и не возобновляется, когда он освобождается

У меня есть приложение Qt сервер-клиент, где клиент отправляет пакеты данных на сервер, а сервер читает их через заданные промежутки времени. Бывает, что клиент отправляет данные быстрее, чем сервер может прочитать, заполняя всю память на стороне сервера. Я использую QAbstractSocket::setReadBufferSize(size), чтобы установить максимальный размер буфера чтения на стороне сервера, и когда он заполняется, передача данных сокета останавливается, и данные буферизируются на стороне клиента, что я и хочу, но проблема заключается в том, когда внутренний буфер чтения сервера QTcpSocket освобождается (больше не заполнен), передача данных между клиентом и сервером не возобновляется.

Я пытался использовать QAbstractSocket::resume(), который, кажется, работает, но документация Qt5.10 гласит:

Продолжается передача данных по сокету. Этот метод следует использовать только после того, как сокет был установлен для приостановки уведомлений и получения уведомления. В настоящее время поддерживается только одно уведомление: QSslSocket::sslErrors(). Вызов этого метода, если сокет не приостановлен, приводит к неопределенному поведению.

Я чувствую, что не должен использовать эту функцию в этой ситуации, но есть ли другое решение? Как я узнаю, что сокет приостановлен? Почему передача данных не продолжается автоматически, когда внутренний буфер чтения QTcpSocket больше не заполнен?

РЕДАКТИРОВАТЬ 1:

Я загрузил источники Qt(5.10.0) и pdb для отладки этой ситуации, и я вижу, что внутренняя функция QAbstractSocket::readData() имеет строку "d-> socketEngine-> setReadNotificationEnabled (true)", которая повторно включает передачу данных, но QAbstractSocket::readData() вызывается только тогда, когда внутренний буфер чтения QTcpSocket пуст (qiodevice.cpp; QIODevicePrivate::read(); строка 1176), и в моей ситуации он никогда не бывает пустым, потому что я читаю его, только когда у него достаточно данные для полного пакета.

Не следует ли вызывать QAbstractSocket::readData(), когда буфер чтения больше не заполнен, а не когда он полностью пуст? А может я что то не так делаю?

1 ответ

Решение

Найден обходной путь!

В источниках 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, если true. Затем я читаю необходимое количество данных из QTcpSocket и в конце я снова вызываю эту функцию, которая, если буфер был заполнен ранее, вызывает QAbstractSocket::setReadBufferSize(size), чтобы установить новый размер буфера и включить внутренние уведомления о чтении. Изменение размера буфера чтения на +/-1 должно быть безопасным, поскольку вы читаете как минимум 1 байт из сокета.

Другие вопросы по тегам