【问题标题】:Protobuf-net serialization on sockets. No parameterless constructor found ProtoException套接字上的 Protobuf-net 序列化。没有找到无参数构造函数 ProtoException
【发布时间】:2012-09-13 11:39:41
【问题描述】:

我创建了一个应用程序,其中使用套接字完成进程间通信。当客户端连接到我创建的服务器并发送序列化消息时,该过程开始。这条消息,我使用 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 服务器接收到数据时,它会将其序列化并将数据发送到客户端。但是这一次,当我尝试使用相同的过程在客户端反序列化数据时,我得到以下异常: “找不到 BaseMessage 的无参数构造函数”

我使用以下代码行反序列化(这是发生异常的地方)。

BaseMessage baseMessage = Serializer.DeserializeWithLengthPrefix<BaseMessage>(networkStream, PrefixStyle.Base128);

这就是消息在服务器上的序列化方式。

Serializer.SerializeWithLengthPrefix(networkStream, baseMessage, PrefixStyle.Base128);

在客户端和服务器之间的连接开始时使用的 NetworkStream 存储在一个对象中,并且该对象存储在字典中。我从字典中的该对象中挑选出相同的 NetworkStream 并使用它将序列化数据发送到客户端(从服务器)。但是会出现上述问题。有什么帮助吗?

提前谢谢...

【问题讨论】:

  • 请问您使用的是哪个版本的 protobuf-net?

标签: c# .net protocol-buffers protobuf-net


【解决方案1】:

在任何 v2 版本中都应该可以正常工作; 2.0.0.480 是当前在 NuGet 上宣传的下载,但也可以使用 2.0.0.580。我已经检查了 1.0.0.280 和 2.0.0.480,但它们都没有出现这种症状,所以我猜你使用的是不同的版本。因此,我的建议是:确保您使用这两个之一(或更高,如果可用)。

对于信息,protobuf-net 不需要[Serializable],但它也没有什么坏处。此外,您的 BaseMessage 构造函数可能应该是 protectedpublicabstract 类型的构造函数没有真正意义)。但由于编译器会自动为您完成所有这些操作,因此您可以简化:

[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 突然想到。它们不必是普遍唯一的:只是在该类型中是唯一的。

【讨论】:

  • Thanx Marc... 我确实在使用 r580,但是包内的文件夹中有很多 dll(CoreOnly、Full 等)我想知道哪些 dll 适合我的应用程序最好的,即使用套接字的桌面应用程序?如果你有时间,请详细说明CoreOnly,Full及其子文件夹(net30 netcore45等)之间的区别。谢谢
  • @WaqasShah 请参阅What Files Do I Need.txt,它包含在 google-code zip 中
  • @WaqasShah 我无法在 r580 上重现它;它现在是否再次正常运行?如果您仍然看到此问题,我可以进行更多调查。
  • 现在我的工作正常......还有一件事,Protobuf 什么时候抛出这个 No parameterless constructor found 异常,尽管它们存在无参数构造函数?是否有与此相关的另一种情况可以引发异常?
  • @Waq 让我想到的是:如果您在受限框架(Silcerlight 等)上运行并且无参数构造函数是非公开的。那么它将无法使用它。但在常规的完整框架上,它可以使用非公共构造函数,甚至完全跳过构造函数。
【解决方案2】:

补充一点,我试图反序列化一个未搜索到源的 MemoryStream,并得到“No parameterless Constructor found for BaseObject”异常,即使该类确实有一个无参数构造函数。

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);
}

【讨论】:

  • 谢谢你,我把头发拉了一个小时。我无法反序列化基类,我什至尝试添加构造函数无济于事。寻找流的开头有效!
【解决方案3】:

我知道这是在回答问题几年后,但对于其他人来说,我只是遇到了一个问题,即我使用抽象基类并得到“没有找到无参数构造函数”对于 {className}" 消息,但出于完全错误的原因...愚蠢的我,我正在反序列化 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; }
}

希望这可以减轻其他人的头痛。

【讨论】:

    【解决方案4】:

    此错误消息似乎涵盖了@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);
    }
    

    【讨论】:

      【解决方案5】:

      错误

      “未找到无参数构造函数”

      我知道,当协议缓冲区中的二进制流不是子类(通过 [ProtoInclude] 链接)时,只是在开始流(二进制数据中的第一个)。

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2015-12-23
        • 1970-01-01
        • 2013-02-19
        • 2010-12-15
        • 1970-01-01
        相关资源
        最近更新 更多