2009-03-23 6 views
19

के साथ अज्ञात प्रकार को Deserialize मेरे पास 2 नेटवर्क वाले ऐप्स हैं जो एक दूसरे को क्रमबद्ध प्रोटोबफ-नेट संदेश भेजना चाहिए। मैं ऑब्जेक्ट्स को क्रमबद्ध कर सकता हूं और उन्हें भेज सकता हूं, हालांकि, मैं यह नहीं समझ सकता कि प्राप्त बाइट को कैसे deserialize करना है।प्रोटोबफ-नेट

मैंने इसके साथ deserialize करने की कोशिश की और यह एक NullReferenceException के साथ विफल रहा।

// Where "ms" is a memorystream containing the serialized 
// byte array from the network. 
Messages.BaseMessage message = 
    ProtoBuf.Serializer.Deserialize<Messages.BaseMessage>(ms); 

मैं धारावाहिक बाइट्स कि संदेश प्रकार आईडी है, जो मैं एक विशाल स्विच बयान में उपयोग कर सकते हैं उम्मीद sublcass प्रकार वापस जाने के लिए होता है से पहले एक हैडर गुजर रहा हूँ। नीचे दिए गए ब्लॉक के साथ, मुझे त्रुटि मिलती है: System.Reflection.TargetInvocationException ---> System.NullReferenceException।

//Where "ms" is a memorystream and "messageType" is a 
//Uint16. 
Type t = Messages.Helper.GetMessageType(messageType); 
System.Reflection.MethodInfo method = 
    typeof(ProtoBuf.Serializer).GetMethod("Deserialize").MakeGenericMethod(t); 
message = method.Invoke(null, new object[] { ms }) as Messages.BaseMessage; 

यहाँ समारोह मैं नेटवर्क पर संदेश भेजने के लिए इस्तेमाल है: आधार वर्ग के साथ

internal void Send(Messages.BaseMessage message){ 
    using (System.IO.MemoryStream ms = new System.IO.MemoryStream()){ 
    ProtoBuf.Serializer.Serialize(ms, message); 
    byte[] messageTypeAndLength = new byte[4]; 
    Buffer.BlockCopy(BitConverter.GetBytes(message.messageType), 0, messageTypeAndLength, 0, 2); 
    Buffer.BlockCopy(BitConverter.GetBytes((UInt16)ms.Length), 0, messageTypeAndLength, 2, 2); 
    this.networkStream.Write(messageTypeAndLength); 
    this.networkStream.Write(ms.ToArray()); 
    } 
} 

इस वर्ग,, मैं serializing कर रहा हूँ:

[Serializable, 
ProtoContract, 
ProtoInclude(50, typeof(BeginRequest))] 
abstract internal class BaseMessage 
{ 
    [ProtoMember(1)] 
    abstract public UInt16 messageType { get; } 
} 

[Serializable, 
ProtoContract] 
internal class BeginRequest : BaseMessage 
{ 
    [ProtoMember(1)] 
    public override UInt16 messageType 
    { 
     get { return 1; } 
    } 
} 


मार्क ग्रेवेल के सुझाव का उपयोग कर फिक्स्ड। मैंने पाठक गुणों से ProtoMember विशेषता को हटा दिया। SerializeWithLengthPrefix का उपयोग करने के लिए भी स्विच किया गया।

//where "this.Ssl" is an SslStream. 
BaseMessage message = 
    ProtoBuf.Serializer.DeserializeWithLengthPrefix<BaseMessage>(
    this.Ssl, ProtoBuf.PrefixStyle.Base128); 

एक वस्तु भेजने के लिए::

[Serializable, 
ProtoContract, 
ProtoInclude(50, typeof(BeginRequest))] 
abstract internal class BaseMessage 
{ 
    abstract public UInt16 messageType { get; } 
} 

[Serializable, 
ProtoContract] 
internal class BeginRequest : BaseMessage 
{ 
    public override UInt16 messageType 
    { 
     get { return 1; } 
    } 
} 

एक वस्तु प्राप्त करने के लिए: यहाँ है कि मैं क्या अब है

//where "this.Ssl" is an SslStream and "message" can be anything that 
// inherits from BaseMessage. 
ProtoBuf.Serializer.SerializeWithLengthPrefix<BaseMessage>(
    this.Ssl, message, ProtoBuf.PrefixStyle.Base128); 
+0

मैं उल्लेख करना भूल गया, मैं मोनो 2.2 में Windows और deserializing पर .NET 3.5 में serializing कर रहा हूँ और एक मंच पर उचित Protobuf शुद्ध DLLs उपयोग कर रहा हूँ। –

+0

मैं इसे पढ़ने के लिए वापस आऊंगा और लगभग आधे घंटे में जवाब पोस्ट करूंगा ... इस समय भागना होगा, क्षमा करें। बीटीडब्लू - अगली रिलीज में गैर-जेनेरिक रैपर शामिल हैं - अभी भी इस समय मेरे लैपटॉप पर। –

+0

बीटीडब्ल्यू - मैं अपनी स्थानीय प्रतिलिपि विलय करने पर काम कर रहा हूं, इसलिए मैं इसे आसान बनाने के लिए परिवर्तन कर सकता हूं। मेरे पास एक उत्कृष्ट परीक्षण-असफल है, लेकिन इसमें नए कोड शामिल हैं, इसलिए यदि यह मदद करता है तो मैं इसे प्रतिबद्ध करने के लिए संतुष्ट हूं (अनदेखा के रूप में चिह्नित)। –

उत्तर

7

पहले; नेटवर्क उपयोग के लिए, SerializeWithLengthPrefix और DeserializeWithLengthPrefix है जो आपके लिए लंबाई (वैकल्पिक रूप से टैग के साथ) को संभालता है। MakeGenericMethod पहली नज़र में ठीक दिखता है; और यह वास्तव में एक आरपीसी स्टैक को लागू करने के लिए किए जा रहे कार्यों के लंबित प्रतिबद्धता के साथ बहुत करीबी संबंध रखता है: लंबित कोड has an override of DeserializeWithLengthPrefix जो कि Func<int,Type> लेता है (अनिवार्य रूप से) Func<int,Type>, किसी प्रकार को एक टैग को हल करने के लिए इसे deserialize करना आसान बनाता है फ्लाई पर अप्रत्याशित डेटा।

यदि संदेश प्रकार वास्तव में BaseMessage और BeginRequest के बीच विरासत से संबंधित है, तो आपको इसकी आवश्यकता नहीं है; यह हमेशा पदानुक्रम में शीर्ष-सबसे अधिक अनुबंध प्रकार पर जाता है और इसके रास्ते को नीचे चलाता है (कुछ तार विवरणों के कारण)।

इसके अलावा - मैं इसे परीक्षण करने के लिए मौका नहीं पड़ा है, लेकिन निम्नलिखित यह परेशान किया जा सकता है:

[ProtoMember(1)] 
public override UInt16 messageType 
{ 
    get { return 1; } 
} 

यह क्रमबद्धता के लिए चिह्नित किया गया है, लेकिन मूल्य स्थापित करने के लिए कोई तंत्र नहीं है। शायद यह मुद्दा है? यहां [ProtoMember] को हटाने का प्रयास करें, क्योंकि यह उपयोगी नहीं है - यह है (जहां तक ​​धारावाहिकता का संबंध है), बड़े पैमाने पर [ProtoInclude(...)] मार्कर का डुप्लिकेट।

+0

मैं इसे एक शॉट दूंगा और परिणामों के साथ वापस टिप्पणी करूंगा। उत्तर देने के लिये धन्यवाद! –

+1

SerializeWithLengthPrefix पर स्विच करने से मेरा कोड छोटा हो गया। :) रीडोनली संपत्ति से ProtoMember विशेषता को हटाने से समस्या ठीक हो गई। धन्यवाद!! –

3

इसे संभालने का एक और तरीका "भारी उठाने" के लिए प्रोटोबफ-नेट का उपयोग करना है, लेकिन अपने संदेश हेडर का उपयोग करना है। नेटवर्क संदेशों को संसाधित करने में समस्या यह है कि उन्हें सीमाओं में तोड़ दिया जा सकता है। इसे आमतौर पर पढ़ने के लिए एक बफर का उपयोग करने की आवश्यकता होती है। यदि आप अपने हेडर का उपयोग करते हैं, तो आप यह सुनिश्चित कर सकते हैं कि प्रोटोबफ-नेट पर इसे सौंपने से पहले संदेश पूरी तरह से है।

एक उदाहरण के रूप:

भेजने के लिए

using (System.IO.MemoryStream ms = new System.IO.MemoryStream()) 
{ 
    MyMessage message = new MyMessage(); 
    ProtoBuf.Serializer.Serialize<BaseMessage>(ms, message); 
    byte[] buffer = ms.ToArray(); 

    int messageType = (int)MessageType.MyMessage; 
    _socket.Send(BitConverter.GetBytes(messageType)); 
    _socket.Send(BitConverter.GetBytes(buffer.Length)); 
    _socket.Send(buffer); 
} 

protected bool EvaluateBuffer(byte[] buffer, int length) 
{ 
    if (length < 8) 
    { 
     return false; 
    } 

    MessageType messageType = (MessageType)BitConverter.ToInt32(buffer, 0); 
    int size = BitConverter.ToInt32(buffer, 4); 
    if (length < size + 8) 
    { 
     return false; 
    } 

    using (MemoryStream memoryStream = new MemoryStream(buffer)) 
    { 
     memoryStream.Seek(8, SeekOrigin.Begin); 
     if (messageType == MessageType.MyMessage) 
     { 
      MyMessage message = 
       ProtoBuf.Serializer.Deserialize<MyMessage>(memoryStream); 
     } 
    } 
} 

बाद विधि एक संचायक बफर पर "की कोशिश की" की जाएगी जब तक पर्याप्त डेटा नहीं था प्राप्त करने के लिए। एक बार आकार की आवश्यकता पूरी होने के बाद, संदेश deserialized किया जा सकता है।

+4

प्रोटोबफ-नेट ने पारित प्रकार के साथ Deserialize करने के लिए एक अधिभार प्रदान किया, यह बहुत फायदेमंद होगा, उदाहरण के लिए ProtoBuf.Serializer.Deserialize (ऑब्जेक्ट टाइप टाइप करें, मेमोरीस्ट्रीम); क्या कोई यह जानता है कि यह संभव है? यदि आपके पास –

+1

RuntimeTypeModel.Default.Deserialize (स्ट्रीम, शून्य, प्रकार) को deserialize करना चाहते हैं, तो यह एक गंदे स्विच स्टेटमेंट से बच जाएगा। –

9
Serializer.NonGeneric.Deserialize(Type, Stream); //Thanks, Marc. 

या

RuntimeTypeModel.Default.Deserialize(Stream, null, Type); 
+0

वास्तव में, या v1 एपीआई (जो अभी भी v2 में काम करता है) के साथ, 'Serializer.NonGeneric.Deserialize (...) '(' टाइप' पैरामीटर लेता है, '' सामान्य प्रकार तर्क नहीं) –

+0

@MarcGravell, धन्यवाद किसी भी तरह से मैंने nonGeneric संपत्ति नहीं देखा -) –

 संबंधित मुद्दे

  • कोई संबंधित समस्या नहीं^_^