2012-01-06 109 views
3

我在下面的代碼與idTCPClient問題從一個telnet服務器讀取緩衝區:空的緩衝區,但IdTCPClient.IOHandler.InputBufferIsEmpty是假

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

我可以通過刪除InputbufferAsString中的Encoding類型來解決這個問題。但是接收的文本包含UTF8文本,並且在我的程序顯示中我有「YX'Y Z)X'X1X(X1 [X.YX/X1X'YX'X1X/Z)Y [X /: Z) YYY X9X(YX1:「text :-(,請幫我 – SadeghAlavizadeh 2012-01-06 18:03:41

+0

一個問題,爲什麼你不使用TidTelnet?這顯然是由telnet控制字符造成的... – whosrdaddy 2012-01-06 18:14:17

+0

因爲idTelnet不支持UTF8,我也想做一些處理可能會在顯示前改變它 – SadeghAlavizadeh 2012-01-06 20:15:37

回答

5

您的代碼試圖讀取InputBuffer中的任意數據塊,並希望它們是完整有效的字符串。它是這樣做的,沒有ANY考慮你正在接收什麼樣的數據。這是多層次的災難處方。

您已連接到Telnet服務器,但使用的是TIdTCPClient,而不是直接使用TIdTelnet,所以你MUST手動解碼所接收任何遠程登錄序列BEFORE然後可以處理任何剩餘的字符串數據。查看TIdTelnet的源代碼。有很多解碼邏輯發生在OnDataAvailable事件觸發之前。所有Telnet序列數據都在內部處理,然後OnDataAvailable事件提供解碼後剩餘的任何非Telnet數據。

一旦你進行了Telnet解碼處理,你必須注意的另一個問題是TEncoding.UTF8只處理正確編碼的COMPLETE UTF-8序列。如果遇到嚴重編碼的序列,或者更重要的是遇到不完整的序列,則返回一個空白字符串。這已經被報告爲一個錯誤(參見QC#79042)。

CheckForDataOnSource()將插入的任何原始字節存儲在那一刻InputBuffer中。 InputBufferAsString()提取InputBuffer在那一刻的任何原始字節,並嘗試使用指定的編碼對它們進行解碼。當您撥打InputBufferAsString()時,InputBuffer中的原始字節很可能並不總是包含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()TIdIOHandler.ReadString()。任何剩餘的字節將在下一次保留在InputBuffer中。爲了達到這個目的,你將不得不無條件地撥打第一個InputBufferIsEmpty()電話,並且只需撥打CheckForDataOnSource(),這樣即使你已經有一些字節,你也總是檢查更多字節。

2)改爲使用TIdIOHandler.ReadChar(),完全擺脫InputBufferIsEmpty()CheckForDataOnSource()的呼叫。缺點是如果UTF-8序列解碼爲UTF-16代理對,則會丟失數據。 ReadChar()可以解碼替代品,但它不能返回對中的第二個字符(我已經開始爲未來版本的Indy返回String而不是Char,因此可以返回完整的代理對)處理新的ReadChar()重載。

+0

Telnet服務器是否返回多字節字符數據? – 2012-01-09 14:47:36

+0

它可以,如果這就是它想要發送的內容,特別是如果實現了Multibyte/Unicode啓用的RFC(如5198和2066)。另外請記住,某些協議(如FTP)基於Telnet,但在Telnet之外具有用於文本編碼的自己的分區,所以即使Telnet協議本身未設置爲Telnet協議本身,也可能包含多字節編碼文本使用文本編碼。 – 2012-01-09 21:41:44

+0

謝謝雷米,你能舉兩個例子嗎? – SadeghAlavizadeh 2012-01-20 11:01:45

1

當你的代碼是正確的,問題是最有可能的是,INPUTBUFFER包含可能含有空字符(#0),這將結束的字符串數據。

嘗試 Remy's解決方案,並檢查你得到的rawbytestring。

編輯

我沒有讀到,OP是從TelnetServer閱讀。 OP應該使用TidTelnet而不是IdTCPClient。

EDIT2

我剛剛看了OP這也解釋了爲什麼他沒有使用TidTelnet的原因an older post

/Daddy

+0

我在那篇老帖子中解釋過如何使用TIdTelnet並仍然完成目標 – 2012-01-07 09:30:09

1

Telnet服務器在每次回車後發送空字符(#0)。這很可能是你看到的。

編碼爲UTF8的空字符仍然是值爲0的單個字節。請檢查您是否正在接收這些字符。