I have problem in below code with idTCPClient for reading buffer from a telnet server:
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;
this procedure run every 1000 milliseconds and when the buffer have a value CheckText called.
this code works but sometimes this return the empty buffer to CheckText.
what’s the problem?
thanks
Your code is attempting to read arbitrary blocks of data from the
InputBufferand expects them to be complete and valid strings. It is doing this without ANY consideration for what kind of data you are receiving. That is a recipe for disaster on multiple levels.You are connected to a Telnet server, but you are using
TIdTCPClientdirectly instead of usingTIdTelnet, so you MUST manually decode any Telnet sequences that are received BEFORE you can then process any remaining string data. Look at the source code forTIdTelnet. There is a lot of decoding logic that takes place before theOnDataAvailableevent is fired. All Telnet sequence data is handled internally, then theOnDataAvailableevent provides whatever non-Telnet data is left over after decoding.Once you have Telnet decoding taken care of, another problem you have to watch out for is that
TEncoding.UTF8only handles properly encoded COMPLETE UTF-8 sequences. If it encounters a badly encoded sequence, or more importantly encounters an incomplete sequence, THE ENTIRE DECODE FAILS and it returns a blank string. This has already been reported as a bug (see QC #79042).CheckForDataOnSource()stores whatever raw bytes are in the socket at that moment into theInputBuffer.InputBufferAsString()extracts whatever raw bytes are in theInputBufferat that moment and attempts to decode them using the specified encoding. It is very possible and likely that the raw bytes that are in theInputBufferwhen you callInputBufferAsString()do not always contain COMPLETE UTF-8 sequences. Chances are that sometimes the last sequence in theInputBufferis still waiting for bytes to arrive in the socket and they will not be read until the next call toCheckForDataOnSource(). That would explain why yourCheckText()function is receiving blank strings when usingTEncoding.UTF8.You should use
IndyUTF8Encoding()instead (Indy implements its own UTF-8 encoder/decoder to avoid the decoding bug inTEncoding.UTF8). At the very least, you will not get blank strings anymore, however you can still lose data when a UTF-8 sequence spans multipleCheckForDataOnSource()calls (incomplete UTF-8 sequences will be converted to?characters). For that reason alone, you should not be usingInputBufferAsString()in this situation (even ifTEncoding.UTF8did work properly). To handle this properly, you should either:1) scan through the
InputBuffermanually, calculating how many bytes constitute COMPLETE UTF-8 sequences only, and then pass that count toInputBuffer.Extract()orTIdIOHandler.ReadString(). Any left over bytes will remain in theInputBufferfor the next time. For that to work, you will have to get rid of the firstInputBufferIsEmpty()call and just callCheckForDataOnSource()unconditionally so that you are always checking for more bytes even if you already have some.2) use
TIdIOHandler.ReadChar()instead and get rid of the calls toInputBufferIsEmpty()andCheckForDataOnSource()altogether. The downside is that you will lose data if a UTF-8 sequence decodes into a UTF-16 surrogate pair.ReadChar()can decode surrogates, but it cannot return the second character in the pair (I have started working on newReadChar()overloads for a future release of Indy that returnStringinstead ofCharso full surrogate pairs can be returned).