2011-07-02 81 views
3

閱讀protobuf的消息時,我使用protobuf的現在幾個星期,但我仍然保持在解析protobuf的消息時的Java越來越例外。異常在Java中

我用C++創建我的protobuf消息和升壓插座將其發送到服務器套接字Java客戶端IST聽那裏。 C++的發送消息的代碼是這樣的:

boost::asio::streambuf b; 
std::ostream os(&b); 

ZeroCopyOutputStream *raw_output = new OstreamOutputStream(&os); 
CodedOutputStream *coded_output = new CodedOutputStream(raw_output); 

coded_output->WriteVarint32(agentMessage.ByteSize()); 
agentMessage.SerializeToCodedStream(coded_output); 

delete coded_output; 
delete raw_output; 

boost::system::error_code ignored_error; 

boost::asio::async_write(socket, b.data(), boost::bind(
     &MessageService::handle_write, this, 
     boost::asio::placeholders::error)); 

正如你可以看到我寫的與WriteVarint32消息的長度,因此Java端應通過使用parseDelimitedFrom知道它應該有多遠閱讀:

AgentMessage agentMessage = AgentMessageProtos.AgentMessage  
           .parseDelimitedFrom(socket.getInputStream()); 

但它沒有幫助,我不斷收到這些類型的異常:

Protocol message contained an invalid tag (zero). 
Message missing required fields: ... 
Protocol message tag had invalid wire type. 
Protocol message end-group tag did not match expected tag. 
While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either than the input has been truncated or that an embedded message misreported its own length. 

這是重要要知道,這些例外情況不會引發每條消息。這只是我收到最多工作的信息的一小部分 - 我仍然想解決這個問題,因爲我不想忽略這些信息。

如果有人能夠幫助我或花費他的想法,我會非常感激。


另一個有趣的事實是我收到的消息數量。我的程序通常在2秒內發出1.000條消息。在20秒內約100.000等。我減少了人爲發送的消息,並且只傳輸6-8條消息,根本沒有錯誤。那麼這可能是Java客戶端套接字端的緩衝問題嗎?

對,假設有60.000條消息,其中5條平均損壞。

+0

也許是一個愚蠢的問題,但有沒有什麼辦法可以在數據中留下填充/超大緩衝區,而不是修剪任何剩餘? –

+0

(這個錯誤肯定可以很容易地由空閒零引起) –

+0

@ Marc-Gravell:什麼是超大緩衝區?其實我不明白你認爲會導致這種情況。也許你可以指出我應該在哪裏尋找這個?順便說一句。我添加了一些其他的例外,我也收到。 –

回答

1

我不熟悉Java API,但我想知道Java如何處理表示消息長度的uint32值,因爲Java只有32位整數。快速瀏覽Java API引用告訴我一個無符號的32位值存儲在一個有符號的32位變量中。那麼如何處理一個無符號的32位值表示消息長度的情況呢?另外,似乎在Java實現中支持varint有符號整數。他們被稱爲ZigZag32/64。 AFAIK,C++版本不知道這種編碼。所以也許你的問題的原因可能與這些事情有關?

+0

也許,但我想這會發生每一次,因爲這些例外只有觸發有時我不知道。 –

0

[我不是一個真正的TCP專家,這可能是遙遠]

問題是,【JAVA] TCP Socket的讀(字節[]緩衝區)將讀取到的結束後返回TCP幀。如果這恰好是中間消息(,我的意思是,原始消息消息),解析器將窒息並拋出一個InvalidProtocolBufferException

不限protobuf的解析呼叫使用CodedInputStream內部(src here),其在情況下,源是InputStream,依賴於read() - ,因此,是受TCP套接字問題。

所以,當你通過套接字填充大量數據時,一些消息必然會被拆分成兩幀 - 這就是它們被破壞的地方。

我在猜測,當你降低消息傳輸速率時(正如你所說的每秒6-8條消息),每個幀在下一個數據片被放入流之前發送,所以每個消息總是得到它的自己的TCP幀,即沒有得到拆分,並沒有得到錯誤。 (或者,也許這只是錯誤是罕見的,低利率你只需要更多的時間來看看它們)

至於解決方案,你最好的選擇是自己處理緩衝區,即從byte[]套接字(可能使用readFully()而不是read(),因爲前者會阻塞,直到有足夠的數據填充緩衝區[或遇到EOF],所以它對中間消息幀結束有阻力),請確保它有足夠的數據被解析爲一個完整的消息,然後將緩衝區提供給解析器。

此外,在這個主題this Google Groups topic一些很好的閱讀 - 這就是我得到的readFully()部分。