Ваш код пытается прочитать произвольные блоки данных из 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
TIdTelnet. Он не заботится о кодировке текста. Его событиеOnDataAvailableпередает вам необработанные байты с использованиемTIdBytes. Если вы хотите, чтобы это было вString, вы должны сами скопировать / декодировать его после получения отTIdTelnet. - person Remy Lebeau   schedule 07.01.2012