Почему мой код не может прочитать количество байтов, доступных на QTcpSocket? Идеи?

У меня есть следующий фрагмент кода в теле цикла, ответственного за чтение данных из QTcpSocket (nntp — указатель на QTcpSocket).

std::vector<char> buffer;
int bytesAvailable = nntp->bytesAvailable();
qDebug() << "bytesAvailable: "<<bytesAvailable;
if(bytesAvailable <= 0) break;
buffer.resize(bytesAvailable);
bytesRead = nntp->read(&buffer[0], bytesAvailable);
qDebug() << (nntp->state() == QAbstractSocket::ConnectedState);
qDebug() << "bytesRead: "<<bytesRead;

Периодически это выводит что-то похожее на следующее:

bytesAvailable:  24
true
bytesRead:  0

И мой код оттуда плохо себя ведет. Это кажется мне очень странным и говорит о том, что я совершенно не понимаю, как работают QTcpSockets. Конечно, если bytesAvailable> 0, то последующее чтение будет означать, что bytesAvailable байты могут быть считаны в буфер, и в этом случае bytesRead должен == bytesAvailable. Или я что-то упустил? В настоящее время я подозреваю, что это может быть какое-то повреждение памяти.

РЕДАКТИРОВАТЬ: добавление некоторых сообщений nntp.errorString () сообщает, что во время этого сбоя «Сетевая операция истекла по тайм-ауту». Мне нужно выяснить, что это значит … (идеи?)

РЕДАКТИРОВАТЬ 2: Кажется, что «Время работы сети истекло» означает, что время чтения истекло. Когда код работает как надо (то есть периодически), я все равно получаю эту «ошибку».

РЕДАКТИРОВАТЬ 3: Полный алгоритмический контекст для приведенного выше фрагмента кода можно найти по этой ссылке.

РЕДАКТИРОВАТЬ 4: немного другая версия функции в РЕДАКТИРОВАНИИ 3, но все еще с теми же проблемами, тем не менее, в эта более новая ссылка

2

Решение

Я обнаружил проблему: QTcpSocket, из которого я пытался прочитать, принадлежал другому потоку. Хотя байты были доступны в соответствии с буфером QIODevice, они не могли быть прочитаны из-за этого.

Следовательно:

* Всегда убедитесь, что сокет, из которого вы хотите прочитать, принадлежит потоку, в котором вы хотите читать *

Чтобы помочь с этим, и, как предложил Тейлор выше, можно воспользоваться инфраструктурой сигналов и слотов QTcpSocket (предпочтительно). Это имеет соответствующую инфраструктуру потоков и теоретически должно сделать все намного проще.

ИЛИ ЖЕ

Будьте супер осторожны и

(a) использовать свой собственный QThread, перемещая объект, содержащий QTcpSocket, в этот QThread

затем,

(b) использовать waitForReadyRead QTcpSocket в цикле блокировки чтения этого другого потока.

Этот последний подход более сложен, так как если кто-то хочет сохранить QTcpSocket для других потоков в будущих операциях чтения и записи, то после того, как он был перемещен в другой поток и обработан этим другим потоком, он должен быть перемещен обратно в основной поток перед тем, как можно когда-нибудь перенести еще одну ветку. — у меня болит голова, я просто пытаюсь это произнести! Конечно, можно предпочесть сохранить один и тот же рабочий поток QTcpSocket так, чтобы его нужно было перемещать только один раз, или просто создавать новый QTcpSocket каждый раз, когда требуется QTcpSocket, перемещая его в свой собственный QThread и затем сразу же удаляя его, когда он закончил. с.

В принципе, если возможно, используйте первый метод сигналов и слотов.

3

Другие решения

Что, вероятно, произойдет, если это bytesAvailable() сообщает размер данных, ожидающих в QIODevice внутренний буфер плюс размер, сообщаемый ОС (например, в Linux, который будет получен ioctl(fd,FIONREAD,&bytesCount)).

Это само по себе имеет смысл, но иметь возможность читать эти байты из QTcpSocket без цикла событий, waitForReadyRead () должен быть вызван в вашем собственном цикле. В противном случае данные из буфера ядра не попадают в QIODeviceБуфер.

Если вашему коду не разрешено блокировать, когда нечего читать в сокете, и вы не хотите преобразовывать его в структуру, управляемую событиями, используйте небольшое значение для waitForReadyReadтайм-аут, так что на практике он будет вести себя так, как будто не блокирует.

Также не забудьте позвонить waitForBytesWritten() а также, если вы также пишете в сокет без цикла событий.

2

Я не сталкивался с этой конкретной проблемой (возможно, потому что я использую другую версию Qt), но я бы порекомендовал попробовать переключиться с цикла на подход, основанный на событиях. Если ваш цикл работает в главном потоке, то у объектов нет возможности доставлять сигналы в очереди (как это может делать класс QTcpSocket внутри), что может быть причиной того, что вы видите «Тайм-аут работы сети»?

Так что подключите некоторые из основных сигналов QTcpSocket:

connect(nntp, SIGNAL(disconnected()),
this, SLOT(onDisconnected()));
connect(nntp, SIGNAL(readyRead()),
this, SLOT(onReadyRead()));
connect(nntp, SIGNAL(error(QAbstractSocket::SocketError)),
this, SLOT(onSocketError(QAbstractSocket::SocketError)));

А затем поместите существующий код «чтения» в onReadyRead.

Возможно, ваша проблема не связана, но недавно я видел похожие проблемы в коде, написанном коллегой, и вот как я это исправил.

2
По вопросам рекламы [email protected]