5

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

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

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

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

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

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

提前致谢...

4

5 回答 5

4

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

对于信息,您不需要[Serializable]protobuf-net,但它也没有伤害。此外,您的BaseMessage构造函数可能应该是protectedpublic对类型的构造函数没有真正意义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跃入脑海。它们不必是普遍唯一的:只是在该类型中是唯一的。

于 2012-09-13T11:54:25.813 回答
3

只是补充一点,我试图反序列化一个没有寻找到源的 MemoryStream 并得到“没有为 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);
}
于 2015-03-11T16:06:17.467 回答
2

我知道这是在回答问题几年后,但对于其他人来说,我只是遇到了一个问题,我正在使用抽象基类并得到“没有为 {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; }
}

希望这可以避免其他人引起我的头痛。

于 2015-07-31T20:57:45.627 回答
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);
}
于 2019-05-30T19:27:44.373 回答
-3

错误

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

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

于 2020-06-04T13:27:40.730 回答