空缓冲区但IdTCPClient.IOHandler.InputBufferIsEmpty为false

时间:2012-01-06 17:01:23

标签: delphi telnet tcpclient indy

我在下面的代码中遇到问题,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 个答案:

答案 0 :(得分:5)

您的代码正在尝试从InputBuffer读取任意数据块,并期望它们是完整且有效的字符串。如果没有 ANY 考虑您收到的数据类型,它会这样做。这是多层次灾难的处方。

您已连接到Telnet服务器,但直接使用TIdTCPClient而不是TIdTelnet,因此您必须手动解码收到的任何Telnet序列 BEFORE 然后您可以处理任何剩余的字符串数据。查看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()或{{1} }。任何剩余字节将在TIdIOHandler.ReadString()下次保留。为了实现这一目标,您必须摆脱第一个InputBuffer调用,并且无条件地调用InputBufferIsEmpty(),这样即使您已经拥有一些字节,也总是要检查更多字节。

2)改为使用CheckForDataOnSource()并完全取消对TIdIOHandler.ReadChar()InputBufferIsEmpty()的调用。缺点是如果UTF-8序列解码为UTF-16代理对,您将丢失数据。 CheckForDataOnSource()可以解码代理,但它不能返回对中的第二个字符(我已经开始处理新的ReadChar()重载,以便将来发布的返回ReadChar()而不是{{1}的Indy所以可以返回完整的代理对)。

答案 1 :(得分:1)

虽然您的代码是正确的,但问题很可能是inputBuffer包含可能包含空字符(#0)的数据,这些字符将结束字符串。

尝试使用 Remy's解决方案,并检查rawbytestring中的内容。

修改

我没有看到OP正在从TelnetServer读取。 OP应该使用TidTelnet而不是IdTCPClient。

<强> EDIT2

我刚读了OP的an older post,解释了他没有使用TidTelnet的原因。

/爸爸

答案 2 :(得分:1)

Telnet服务器在每次回车后发送一个空字符(#0)。这很可能就是你所看到的。

编码为UTF8的空字符仍然是单个字节,值为0.检查是否是您正在接收的内容。