Передача данных 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 байт из сокета.