пустой буфер, но IdTCPClient.IOHandler.InputBufferIsEmpty имеет значение false - PullRequest
3 голосов
/ 06 января 2012

У меня проблема в приведенном ниже коде с idTCPClient для чтения буфера с сервера telnet:

procedure TForm2.ReadTimerTimer(Sender: TObject);
var
   S: String; 
begin
   if IdTCPClient.IOHandler.InputBufferIsEmpty then
   begin
     IdTCPClient.IOHandler.CheckForDataOnSource(10);
     if IdTCPClient.IOHandler.InputBufferIsEmpty then Exit;
   end;
   s := idTCPClient.IOHandler.InputBufferAsString(TEncoding.UTF8);
   CheckText(S);
end;

эта процедура выполняется каждые 1000 миллисекунд, и когда в буфере вызывается значение CheckText.

этот код работает, но иногда он возвращает пустой буфер в CheckText.

в чем проблема?

спасибо

Ответы [ 3 ]

5 голосов
/ 07 января 2012

Ваш код пытается прочитать произвольные блоки данных из InputBuffer и ожидает, что они будут полными и допустимыми строками.Он делает это без ЛЮБОГО рассмотрения того, какие данные вы получаете.Это рецепт для аварии на нескольких уровнях.

Вы подключены к серверу Telnet, но вы используете TIdTCPClient вместо использования TIdTelnet, поэтому вы ДОЛЖНЫ декодировать вручнуюлюбые полученные последовательности Telnet ДО вы можете затем обработать любые оставшиеся строковые данные.Посмотрите на исходный код TIdTelnet.Существует много логики декодирования, которая имеет место до того, как событие OnDataAvailable сработало.Все данные последовательности Telnet обрабатываются внутренне, затем событие OnDataAvailable предоставляет все данные, не относящиеся к Telnet, оставшиеся после декодирования.

После того, как вы позаботились о декодировании Telnet, следует обратить внимание на еще одну проблему:что TEncoding.UTF8 обрабатывает только правильно закодированные COMPLETE UTF-8 последовательности.Если он встречает плохо закодированную последовательность или, что еще важнее, встречает неполную последовательность, ВЕСЬ ДЕКОД ДЕЙСТВУЕТ НЕ и возвращает пустую строку.Об этом уже сообщалось как об ошибке (см. QC # 79042 ).

CheckForDataOnSource() сохраняет все необработанные байты в сокете в этот момент в InputBuffer.InputBufferAsString() извлекает любые необработанные байты в InputBuffer в этот момент и пытается декодировать их, используя указанную кодировку.Очень возможно и вероятно, что необработанные байты, которые находятся в InputBuffer при вызове InputBufferAsString(), не всегда содержат COMPLETE UTF-8 последовательностей.Скорее всего, иногда последняя последовательность в InputBuffer все еще ожидает поступления байтов в сокет, и они не будут считываться до следующего вызова CheckForDataOnSource().Это объясняет, почему ваша функция CheckText() получает пустые строки при использовании TEncoding.UTF8.

Вместо этого следует использовать IndyUTF8Encoding() (Indy реализует свой собственный кодер / декодер UTF-8, чтобы избежать ошибки декодирования в TEncoding.UTF8).По крайней мере, вы больше не будете получать пустые строки, однако вы все равно можете потерять данные, когда последовательность UTF-8 охватывает несколько вызовов CheckForDataOnSource() (неполные последовательности UTF-8 будут преобразованы в ? символов).Только по этой причине вам не следует использовать InputBufferAsString() в этой ситуации (даже если TEncoding.UTF8 работал правильно).Для правильной обработки вы должны:

1) отсканировать вручную InputBuffer, рассчитав, сколько байтов составляет COMPLETE только последовательности UTF-8, а затем передать этот счет InputBuffer.Extract() или TIdIOHandler.ReadString().Любые оставшиеся байты останутся в InputBuffer в следующий раз.Чтобы это сработало, вам придется избавиться от первого InputBufferIsEmpty() вызова и просто безоговорочно позвонить CheckForDataOnSource(), чтобы вы всегда проверяли наличие дополнительных байтов, даже если у вас уже есть некоторые.

2) useTIdIOHandler.ReadChar() вместо этого и избавьтесь от звонков на InputBufferIsEmpty() и CheckForDataOnSource() в целом.Недостатком является то, что вы потеряете данные, если последовательность UTF-8 декодируется в суррогатную пару UTF-16.ReadChar() может декодировать суррогаты, но не может вернуть второй символ в паре (я начал работать над новыми ReadChar() перегрузками для будущего выпуска Indy, которые возвращают String вместо Char, поэтому полные суррогатные пары могут бытьвозвращается).

1 голос
/ 06 января 2012

Серверы Telnet отправляют нулевой символ (# 0) после каждого возврата каретки.Скорее всего, это то, что вы видите.

Нулевой символ, закодированный в UTF8, по-прежнему представляет собой один байт со значением 0. Проверьте, является ли это тем, что вы получаете.

1 голос
/ 06 января 2012

Хотя ваш код верен, проблема, скорее всего, состоит в том, что inputBuffer содержит данные, которые могут содержать нулевые символы (# 0), которые заканчивают строку.

Попробуйте Решение Реми ,и проверьте, что вы получаете в строке rawbytestring.

Редактировать

Я не читал, что OP считывал с TelnetServer.OP должен использовать TidTelnet вместо IdTCPClient.

Edit2

Я только что прочитал более старый пост OP, который объясняет причину, по которой он не используетТидТелнет.

/ Папочка

...