java - 带有长字符串的 protobuf 中的奇怪行为

标签 java c++ serialization protocol-buffers

我正在尝试将数据从客户端发送到服务器。这两个应用程序都是用 java 编写的。但是他们在 SWIG Wrappers 上使用用 c++ 实现的 tls 层。 tls 层需要来自客户端的字符串,将其传输到服务器端并通知 java 服务器应用程序(并传递字符串)。但是,此字符串应包含序列化数据。不知何故,我很难使用 protobuf 来序列化数据。我想使用一个名为 ToDoListMessage 的 java protobuf 类。 protobuf 看起来像这样:

message ToDoListMessage{  
    optional string user = 1;  
    optional string token = 2;
}

但是生成的java类无法解析之前序列化的数据:

com.google.protobuf.InvalidProtocolBufferException: Protocol message tag had invalid wire type.

我目前没有向服务器发送数据。仅在客户端测试序列化和解析部分:

ToDoListMessageProto msg = ToDoListMessageProto.newBuilder().setUser("test").setToken("38632735722755").build();        

byte b [] = msg.toByteArray();  
String sMsg = Arrays.toString(b);   
System.out.println("send message = " + sMsg);
ToDoListMessageProto outputmessage;         
outputmessage = ToDoListMessageProto.parseFrom(sMsg.getBytes());

消息看起来像:

[10, 4, 116, 101, 115, 116, 18, 14, 51, 56, 54, 51, 50, 55, 51, 53, 55, 50, 50, 55, 53, 53]

我尝试了什么:

1) 到目前为止我找到的所有解决方案都表明这个问题可以通过使用 CodedOutputStream 来解决。但是 tls 层需要一个字符串,而不是一个流。但是我也尝试以下:

ByteArrayOutputStream bos = new ByteArrayOutputStream();
CodedOutputStream cos = CodedOutputStream.newInstance(bos);
msg.writeTo(cos);   
cos.flush();
byte b [] = msg.toByteArray();              
String sMsg = Arrays.toString(b);   

但是对于这个解析我得到了和上面一样的错误:

CodedInputStream cis = CodedInputStream.newInstance(sMsg.getBytes());
ToDoListMessageProto message = ToDoListMessageProto.parseFrom(cis);

2) 我还尝试使用 UTF8 编码的字符串而不是类似数组的字符串:

String sMsg = new String(b);

在这种情况下,应用程序的行为更加奇怪。对于短“ token ”(例如小于 129 位)解析有效,但对于长 token 失败:

com.google.protobuf.InvalidProtocolBufferException: While parsing a protocol message, the input ended unexpectedly in the middle of a field. This could mean either than the input has been truncated or that an embedded message misreported its own length.

我真的不知道为什么。目前 token 只包含数字。

有谁知道如何从 protobuf 中获取可以正确解析的序列化字符串的解决方案?

再次声明:此测试中不涉及 tls 传输。目前一切都在客户端完成。

更新:

因为我直接从 Protobuf 消息中获取字节数组,所以不可能传递编码。我发现消息还有一个 toByteString 方法,但在此 ByteString 上使用 toStringUtf8 似乎也不起作用:

String sMsg = msg.toByteString().toStringUtf8();
System.out.println("send message = " + sMsg);
ToDoListMessageProto outputmessage;         
outputmessage = ToDoListMessageProto.parseFrom(sMsg.getBytes());

我收到相同的错误消息(如果我使用长 token 或短 token 会有所不同,请参见上文)

最佳答案

将 java String 转换为字节数组并返回总是需要指示要使用的编码。如果省略此指示符,则只有 7 位字符(编码为“US-ASCII”,自 java7:StandardCharsets.US_ASCII 起)被正确转换。如果你想序列化 UTF-8 字符串:

        String inputStr = "öäü";
        byte[] serialized = inputStr.getBytes( StandardCharsets.UTF_8);
        System.out.println( "Number of bytes: " + serialized.length);

        StringBuilder sb = new StringBuilder();
        for (byte b : serialized)
        {
            sb.append(String.format("%02X ", b));
        }
        System.out.println( "Bytes: " + sb.toString());
        String back = new String( serialized, StandardCharsets.UTF_8);
        System.out.println( "Back: " + back);

给出输出:

Number of bytes: 6
Bytes: C3 B6 C3 A4 C3 BC 
Back: öäü

关于java - 带有长字符串的 protobuf 中的奇怪行为,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/36164247/

相关文章:

java - 在 Spring Batch 中将 writer 添加到相同的 datfile 中

c++ - vim 插件注释 block

c++ 项目构建成功但给出 g++ 编译器错误消息

java - 为什么我的 native JNI 代码是顺序运行而不是并发运行?

java - 从非 Activity 课中打开 Sherlock Fragment Activity ?

java - 为什么我的 JFrame 程序不会更改背景颜色?

c++ - =delete 有哪些用途?

c# - 是否可以使用反序列化进行构造函数注入(inject)?

c# - 为值对象序列化自定义 NewtonSoft.Json

java - 获取SerializationService,通过序列化进行克隆