Google.Protobuf.InvalidProtocolBufferException:协议消息包含无效标签(零)

Posted

技术标签:

【中文标题】Google.Protobuf.InvalidProtocolBufferException:协议消息包含无效标签(零)【英文标题】:Google.Protobuf.InvalidProtocolBufferException: Protocol message contained an invalid tag (zero) 【发布时间】:2017-11-09 04:32:21 【问题描述】:

我的学校项目有问题,我使用 Protobuf 库,但出现以下错误:

Google.Protobuf.InvalidProtocolBufferException" 协议消息包含无效标签(零)。

我的协议消息包装是:

syntax = "proto3";
package CardGameGUI.Network.Protocol.Message;

message WrapperMessage 
enum MessageType 
    HELLO_MESSAGE = 0;
    JOIN_ROOM_MESSAGE = 1;
    JOIN_ROOM_RESPONSE_MESSAGE = 2;


MessageType type = 1;
bytes       payload = 2;

我用它来发送消息:

    public void SendObject<T>(Protocol.Message.WrapperMessage.Types.MessageType type, T messageObject)
    
        byte[] message;

        // Serialize message
        using (var stream = new MemoryStream())
        
            ((IMessage)messageObject).WriteTo(stream);

            message = stream.GetBuffer();
        

        byte[] wrapper = new Protocol.Message.WrapperMessageType = type, Payload = Google.Protobuf.ByteString.CopyFrom(message).ToByteArray();

        Connection.SendObject<byte[]>("ByteMessage", wrapper);
    

还有我的服务器处理程序:

private void IncommingMessageHandler(PacketHeader header, Connection connection, byte[] message)
    
        Protocol.Message.WrapperMessage wrapper = Protocol.Message.WrapperMessage.Parser.ParseFrom(message);

        switch (wrapper.Type)
        
            case Protocol.Message.WrapperMessage.Types.MessageType.HelloMessage:
                GetClient(connection.ConnectionInfo.NetworkIdentifier).MessageHandler(Protocol.Message.HelloMessage.Parser.ParseFrom(wrapper.Payload.ToByteArray()));

                break;
        
    

包装消息完全反序列化,并且类型正确匹配,但在我的 Payload 处理时,异常弹出。

我做了坏事吗?

编辑:消息有效载荷的小屏幕

【问题讨论】:

【参考方案1】:

问题可能是您使用了GetBuffer 而没有使用已知长度。 GetBuffer 返回 oversized 后备数组。流的.Length 之后的数据是垃圾,不应被消耗 - 它通常(但不总是)为零,这就是您所看到的。

要么使用ToArray() 而不是GetBuffer(),要么跟踪流的.Length 并且只消耗那么大的缓冲区。


另一种可能性是“成帧” - 看起来您正在处理数据包,但如果这是 TCP,则无法保证您收到的块与您发送的块大小相同。如果您通过 TCP 发送多条消息,则需要实现自己的框架(通常通过长度前缀,因为您正在谈论二进制数据)。


顺便说一句,这不是 protobuf-net。


如果这些都不是问题:检查您收到的数据是否完全(逐字节)您发送的数据(包括长度)。数据很容易被 IO 代码损坏或错误分块。

【讨论】:

谢谢,ToArray 是解决方案! :D【参考方案2】:

在这种情况下我遇到了这个问题 因为我的序列化字节流丢失了 varint lenth

例如,如果我序列化一个 672 字节的“Person.proto”消息 如果我反序列化 672 字节会遇到错误

解决策略是在 672bytes 中添加 varint len,这样就可以得到一个 674bytes 的流

额外的数据是 672 的“varint”代码,即 160,5

你可以通过函数获取varint字节

public static byte[] VarInt(int value)

    //data len
    List<byte> varIntBuffer = new List<byte>();
    int index = 0;
    while (true)
    
        if ((value & ~0x7f) == 0)
        
            varIntBuffer.Add((byte)(value & 0x7f));
            break;
        
        else
        
            varIntBuffer.Add((byte)((value & 0x7f) | 0x80));
            value = value >> 7;
        
        index++;
    
    return varIntBuffer.ToArray();

【讨论】:

【参考方案3】:

我在尝试反序列化已初始化为固定大小的字节数组时遇到了同样的问题,但是有一个错误,这意味着我没有用原始字节填充数组(所以当我用零填充字节数组时)正在尝试反序列化)。

事实证明,我在测试用例中两次从 JMS BytesMessage 读取字节,但在第二次读取之前没有调用 BytesMessage.reset()。

我猜如果尝试从InputStream 读取两次而不调用reset(),您可能会遇到类似的错误

【讨论】:

以上是关于Google.Protobuf.InvalidProtocolBufferException:协议消息包含无效标签(零)的主要内容,如果未能解决你的问题,请参考以下文章