Ваш код пытается прочитать произвольные блоки данных из 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