我创建了一个使用套接字完成进程间通信的应用程序。当客户端与我创建的服务器连接并发送序列化消息时,该过程开始。这条消息,我使用Protobuf-net进行序列化,使用SerializeWithLengthPrefix并使用DeserializeWithLengthPrefix对其进行反序列化。客户端将消息发送到完全反序列化的服务器,但在服务器到客户端的情况下却不是这样。
主类是BaseMessage,它是抽象的。
[Serializable, ProtoContract, ProtoInclude(5001, typeof(LogonMessage))]
abstract public class BaseMessage
{
public BaseMessage()
{
}
abstract public int MessageType { get; }
}
LogonMessage实现了BaseMessage类。
[Serializable, ProtoContract]
public class LogonMessage : BaseMessage
{
public LogonMessage()
{
}
[ProtoMember(1)]
public string Broker { get; set; }
[ProtoMember(2)]
public int ClientType { get; set; }
public override int MessageType
{
get { return 1; }
}
}
在初始握手之后,客户端在protobuf-net的帮助下请求序列化一些服务,而我端的本地服务器通过从Web上的另一台服务器请求数据来服务它。从客户端到服务器的此消息传输完美无瑕。
当我的服务器从Web服务器接收数据时,它将其序列化并将数据发送到客户端。但是这一次,当我尝试使用相同的过程反序列化客户端的数据时,我得到以下异常: “没有为BaseMessage找到无参数构造函数”
我使用以下代码行反序列化(这是异常发生的地方)。
BaseMessage baseMessage = Serializer.DeserializeWithLengthPrefix<BaseMessage>(networkStream, PrefixStyle.Base128);
这就是消息在服务器上的序列化方式。
Serializer.SerializeWithLengthPrefix(networkStream, baseMessage, PrefixStyle.Base128);
在客户端和服务器之间的连接开始时使用的NetworkStream存储在一个对象中,该对象存储在一个字典中。我从字典中的该对象中选择相同的NetworkStream,并使用它将序列化数据发送到客户端(从服务器)。但是出现上述问题。有什么帮助吗?
提前致谢...
答案 0 :(得分:4)
在任何v2版本中都可以正常工作; 2.0.0.480是目前在NuGet上公布的下载,但也可以使用2.0.0.580。我已经检查了1.0.0.280和2.0.0.480,它们都没有显示这个症状,所以我猜你正在使用不同的版本。因此,我的建议是:确保您使用其中一个(或更高,如果可用)。
有关信息,protobuf-net不需要[Serializable]
,但它也不会受到伤害。此外,您的BaseMessage
构造函数应该是protected
(public
对abstract
类型的构造函数没有意义。但由于编译器会自动为您完成所有 ,因此您可以简化:
[ProtoContract, ProtoInclude(5001, typeof(LogonMessage))]
abstract public class BaseMessage
{
abstract public int MessageType { get; }
}
[ProtoContract]
public class LogonMessage : BaseMessage
{
[ProtoMember(1)]
public string Broker { get; set; }
[ProtoMember(2)]
public int ClientType { get; set; }
public override int MessageType
{
get { return 1; }
}
}
其他想法:5001
有点偏高;从低价值的包含数字中获得更高的效率。 1
跳出了脑海。它们不必是普遍唯一:在该类型中只是唯一的。
答案 1 :(得分:2)
只是添加,我试图反序列化一个未被寻找到原点的MemoryStream,并得到了#34;没有为BaseObject找到无参数构造函数&#34;异常,即使该类确实有一个无参数构造函数。
var tdp = new DerivedObject("Test");
using (var ms = new MemoryStream())
{
Serializer.Serialize(ms,tdp);
//was missing this line
ms.Seek(0, SeekOrigin.Begin);
var tdp2 = Serializer.Deserialize<DerivedObject>(ms);
}
答案 2 :(得分:2)
我知道这是问题得到解答后的几年,但对于其他人的价值,我只是遇到了一个问题,我正在使用抽象基类并获取& #34;找不到{className}&#34;的无参数构造函数;消息,但是出于一个完全错误的原因...愚蠢的我,我从没有任何东西的MemoryStream中反序列化(显然,当时我并不知道)。令人沮丧的愚蠢。正确的错误消息应该是:"id10t error - cannot deserialize nothing, dum dum!"
。
因为错误消息指的是一些构造函数问题,以及抽象类的使用,这让我觉得我错误地使用了库(即 - 没有正确装饰具有相应Proto *属性的类/成员等。 )
重点是:把这个错误信息带到一粒盐 - 你的异常的根本原因可能就像我的;退后一步,看看你在哪里访问protobuf-net API周围的活动部分,确保在将头撞到墙上之前没有什么傻傻的进行,搜索一个库(那个库)在这种情况下,实际上是从外面正常工作。
如何重现错误?有一堆嘈杂的代码会分散您的实际错误,与红鲱鱼错误消息串联......就像这样:
[TestClass]
public class UnitTestJammieJawns
{
[TestMethod]
public void ProtobufAbstract_TestMethod()
{
var sub = new Sub() { AbstractInt = 234 , OtherInt32 = 987, SomeString = "qwer" };
byte[] buffer = null;
using (var ms = new MemoryStream())
{
ProtoBuf.Serializer.Serialize(ms, sub); // works - hooray!
buffer = ms.ToArray(); // we've got binary content, people!
}
Sub obj = null;
using (var ms = new MemoryStream()) // woops... you forgot the provide the serialized object - should be: new MemoryStream(buffer)
{
obj = ProtoBuf.Serializer.Deserialize<Sub>(ms); // <- throws exception (this is good) with misleading message (this is not so good)
}
}
}
[ProtoContract]
[ProtoInclude(1, typeof(Sub))]
public abstract class Super
{
public abstract int AbstractInt { get; set; }
[ProtoMember(1)]
public string SomeString { get; set; }
}
[ProtoContract]
public class Sub : Super
{
[ProtoMember(2)]
private int asdf;
public override int AbstractInt
{
get
{
return asdf;
}
set
{
asdf = value;
}
}
[ProtoMember(3)]
public int OtherInt32 { get; set; }
}
希望这可以挽救别人对我造成的痛苦。
答案 3 :(得分:0)
对于类似@isandburn和@JP Tissot所指出的各种不同的错误,该错误消息似乎可以抓住一切。
另一种情况是,如果您对不是protobuf的数据进行反序列化。
var tdp = new DerivedObject("Test");
using (var ms = new MemoryStream())
{
JsonSerializer.Serialize(ms,tdp); //should be protobuf
ms.Seek(0, SeekOrigin.Begin);
var tdp2 = Serializer.Deserialize<DerivedObject>(ms);
}
答案 4 :(得分:-3)
错误
“未找到无参数的构造函数”
我明白了,当协议缓冲区中的二进制流不是子类(通过[ProtoInclude]链接)时,只是开始流(二进制数据中的第一个)。