2011-06-03 122 views
7

我將讀取從C#服務器發送的C++客戶端中的套接字中連續存儲的消息。我希望我能讀到這樣一條消息的大小:protobuf:從C#讀取C++中的消息

google::protobuf::uint32 m; 
coded_input->ReadVarint32(&m); 
cout << m << endl; 

然後我想讀消息:

Person person; 
CodedInputStream::Limit limit = coded_input->PushLimit(m); 
person.ParseFromCodedStream(coded_input); 
coded_input->PopLimit(limit); 

的C#代碼如下所示:

var person = new Person { Id = 123456, Name = "Fred", Address = new Address { Line1 = "Flat 1", Line2 = "The Meadows ar garą " } }; 
Stream str = new NetworkStream(socket); 
Serializer.SerializeWithLengthPrefix(str, person, PrefixStyle.Fixed32); 

它不起作用。

我得到的C++錯誤(43是COUT 米< < ENDL的結果;)(ID,姓名,地址是在人的所有字段)

libprotobuf ERROR 谷歌/protobuf/message_lite.cc:123] 無法解析類型爲「Person」的郵件 ,因爲它缺少必填字段: ID,名稱,地址

當我沒有閱讀變體並從coded_input中直接解析消息時,一切正常(當我更改SerializeWithLengthPrefix以在服務器代碼中序列化時)。不過,我需要一種方法來區分連續的消息,所以我需要知道我要讀取的消息的大小。我只是不知道如何發送尺寸。

我嘗試:

Stream buf = new MemoryStream(); 
Serializer.Serialize(buf, person); 
ProtoWriter writer = new ProtoWriter(str, null); 
ProtoWriter.WriteInt32((int)buf.Length, writer); 

但然後我得到:

未處理的異常: ProtoBuf.ProtoException:與線型 無在位置0處 protobuf的無效 序列化操作。 ProtoWriter.WriteInt32(Int32 value,ProtoBuf.ProtoWriter writer) [0x00000] in:0
at protobuftest.MainClass.Main (System.String []參數)[0x00097]在 /home/lorddidger/studia/csharp/protobuf-test/protobuf-test/Main.cs:31

我不能夠以這種方式發送任何int。哪裏不對?


更新: 其實,我的目標是找到一種方法,與protobuf的傳遞一個整數(大小)。我需要任何示例(包括C++和C#)如何處理,因爲我不瞭解protobuf中的所有細節。

我注意到,SerializeWithLengthPrefix發送前綴(4 uint32),看起來像:大小0 0 0.大小是序列化後的消息字節數(我猜)。我認爲PrefixStyle.Fixed32說消息前只有一個uint32,但有4個!

最後,我以爲你應該使用ProtoWriter.WriteInt32((int)buf。長度,作家)傳輸整數,因爲我遵循somwhere in the internet的建議,我認爲這是一個與C++相關的解決方法。現在我看到你不應該寫varnish - 他們與引擎有關,而且花時間複雜。

我應該發送帶有一個int?我應該如何區分第一個存儲大小的下一個消息?

我看到C#可以hadle前綴罰款,但沒有任何兼容C++和C#的方式來消息的狀態的大小?否則,沒有任何我會發現不合理的排隊消息的方式。

回答

3

你的第一實施例包括僅有一個長度; 「帶長度前綴」實際上是以protobuf兼容的流進行編碼的。

如果從C++進行解碼,讀 varints;首先是場號和線型;第二個是長度。第一個是打包成3比特線型,其餘的是場號。您也可以指定字段0跳過 - 如果沒有檢查,我不記得。


更新;我檢查了這一點,寫入沒有字段編號(只是varint長度前綴)的數據,然後使用兩個不同的API(分別使用Deserialize)和通過DeserializeItems作爲可枚舉塊讀取C#中的數據。我不得不修復一個bug在後者,但下面將從下一個代碼推(注意工作:只有閱讀代碼有一個解決方法,所以如果你是用C#編寫,並用C閱讀++這不會影響你):

using (var ms = new MemoryStream()) 
{ 
    // write data with a length-prefix but no field number 
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 1 }, PrefixStyle.Base128, 0); 
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 2 }, PrefixStyle.Base128, 0); 
    Serializer.SerializeWithLengthPrefix(ms, new Foo { Bar = 3 }, PrefixStyle.Base128, 0); 

    ms.Position = 0; 
    Assert.AreEqual(9, ms.Length, "3 lengths, 3 headers, 3 values"); 

    // read the length prefix and use that to limit each call 
    TypeModel model = RuntimeTypeModel.Default; 
    int len, fieldNumber, bytesRead; 
    List<Foo> foos = new List<Foo>(); 
    do 
    { 
     len = ProtoReader.ReadLengthPrefix(ms, false, PrefixStyle.Base128, out fieldNumber, out bytesRead); 
     if (bytesRead <= 0) continue; 

     foos.Add((Foo)model.Deserialize(ms, null, typeof(Foo), len)); 

     Assert.IsTrue(foos.Count <= 3, "too much data!"); 
    } while (bytesRead > 0); 

    Assert.AreEqual(3, foos.Count); 
    Assert.AreEqual(1, foos[0].Bar); 
    Assert.AreEqual(2, foos[1].Bar); 
    Assert.AreEqual(3, foos[2].Bar); 

    // do it using DeserializeItems 
    ms.Position = 0; 

    foos.Clear(); 
    foreach (var obj in model.DeserializeItems<Foo>(ms, PrefixStyle.Base128, 0)) 
    { 
     foos.Add(obj); 
     Assert.IsTrue(foos.Count <= 3, "too much data!"); 
    } 
    Assert.AreEqual(3, foos.Count); 
    Assert.AreEqual(1, foos[0].Bar); 
    Assert.AreEqual(2, foos[1].Bar); 
    Assert.AreEqual(3, foos[2].Bar); 
} 
+0

Awww Marc,它只有幾千行代碼...你不記得了嗎? ;) – IAbstract 2011-06-03 18:24:27

+0

@IAbstract如何我希望* *它是隻有幾千:)添加在運行模式,充分IL編譯器,V1執行,各個平臺版本(CF,SL,WP7,單聲道,等等),原始測試套件,v2元模型測試套件,代碼生成(.proto),VS工具,...讓我們只是說它加起來:) – 2011-06-03 20:51:03

+0

這次我會削減你一些鬆懈... :) – IAbstract 2011-06-03 21:58:48