пустой буфер, но IdTCPClient.IOHandler.InputBufferIsEmpty имеет значение false

У меня проблема в приведенном ниже коде с 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.

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

Благодарность


person SadeghAlavizadeh    schedule 06.01.2012    source источник
comment
Я могу решить эту проблему, удалив тип кодировки в InputbufferAsString. Но полученный текст содержит текст UTF8, и в отображении моей программы у меня есть YX'Y Z) X'X1X (X1 [X.YX / X1X 'YX'X1X / Z) Y [X /: Z) YYY X9X (YX1: текст :-(, пожалуйста, помогите мне   -  person SadeghAlavizadeh    schedule 06.01.2012
comment
Один вопрос, почему вы не пользуетесь TidTelnet? Это явно вызвано контрольными символами telnet ...   -  person whosrdaddy    schedule 06.01.2012
comment
потому что idTelnet не поддерживает UTF8, а также я хочу выполнить некоторые процессы с текстом буфера и, возможно, изменить его перед отображением.   -  person SadeghAlavizadeh    schedule 07.01.2012
comment
Это не веская причина не использовать TIdTelnet. Он не заботится о кодировке текста. Его событие OnDataAvailable передает вам необработанные байты с использованием TIdBytes. Если вы хотите, чтобы это было в String, вы должны сами скопировать / декодировать его после получения от TIdTelnet.   -  person Remy Lebeau    schedule 07.01.2012


Ответы (3)


Ваш код пытается прочитать произвольные блоки данных из 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 вручную, подсчитав, сколько байтов составляют только ЗАВЕРШЕННЫЕ последовательности UTF-8, а затем передайте это количество в InputBuffer.Extract() или TIdIOHandler.ReadString(). Любые оставшиеся байты останутся в InputBuffer в следующий раз. Чтобы это сработало, вам нужно будет избавиться от первого вызова InputBufferIsEmpty() и просто вызвать CheckForDataOnSource() без каких-либо условий, чтобы вы всегда проверяли наличие дополнительных байтов, даже если они у вас уже есть.

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

person Remy Lebeau    schedule 07.01.2012
comment
Возвращает ли сервер Telnet многобайтовые символьные данные? - person Marcus Adams; 09.01.2012
comment
Может, если это то, что он хочет отправить, особенно если реализованы RFC с поддержкой Multibyte / Unicode, такие как 5198 и 2066. Также имейте в виду, что некоторые протоколы, такие как FTP, основаны на Telnet, но имеют свою собственную сегментацию для кодирования текста за пределами Telnet, поэтому данные Telnet могут содержать многобайтовый закодированный текст, даже если сам протокол Telnet не настроен на использовать текстовые кодировки. - person Remy Lebeau; 10.01.2012
comment
спасибо, Реми, можешь дать мне пример для каждого из двух способов? - person SadeghAlavizadeh; 20.01.2012
comment
для каждого из двух способов, что вы имеете в виду? - person Remy Lebeau; 21.01.2012
comment
InputBuffer и TIdIOHandler.ReadChar () путь - person SadeghAlavizadeh; 22.01.2012

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

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

Изменить

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

Edit2

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

/Папочка

person whosrdaddy    schedule 06.01.2012
comment
Я объяснил ему в том старом посте, как использовать TIdTelnet и по-прежнему достигать своей цели. - person Remy Lebeau; 07.01.2012

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

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

person Marcus Adams    schedule 06.01.2012