我在下面的代码中遇到问题,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。
问题是什么?
感谢
答案 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.检查是否是您正在接收的内容。