Warning: file_get_contents(/data/phpspider/zhask/data//catemap/6/cplusplus/124.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
Java 长字符串protobuf中的奇怪行为_Java_C++_Serialization_Protocol Buffers - Fatal编程技术网

Java 长字符串protobuf中的奇怪行为

Java 长字符串protobuf中的奇怪行为,java,c++,serialization,protocol-buffers,Java,C++,Serialization,Protocol Buffers,我正在尝试将数据从客户端发送到服务器。这两个应用程序都是用java编写的。但是他们使用了在C++上实现的TLS层,而不是SWIG包装器。tls层需要来自客户端的字符串,将其传输到服务器端并通知java服务器应用程序(并传递该字符串)。但是,此字符串应包含序列化数据。不知何故,我很难使用protobuf来序列化数据。我想使用一个名为ToDoListMessage的java protobuf类。protobuf如下所示: message ToDoListMessage{ optional

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

message ToDoListMessage{  
    optional string user = 1;  
    optional string token = 2;
}
但生成的java类无法解析之前序列化的数据:

com.google.protobuf.InvalidProtocolBufferException:协议消息 标记具有无效的导线类型

我当前没有将数据发送到服务器。只需在客户端上测试序列化和解析部分:

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) 到目前为止,我找到的所有解决方案都表明,这个问题可以通过使用
codedOutStream
来解决。但是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);
在这种情况下,应用程序的行为更加奇怪。简而言之,“令牌”(例如小于129位)解析工作正常,但长令牌解析失败:

com.google.protobuf.InvalidProtocolBufferException:在解析 协议消息,输入意外地在A的中间结束。 领域这可能意味着输入被截断或 嵌入的消息歪曲了它自己的长度

我真的不知道为什么。目前,令牌仅包含数字

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

再次说明:本测试中没有涉及tls传输。目前一切都在客户端完成。

更新:

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

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

我得到相同的错误消息(如果我使用长或短标记,则不同,请参见上文)

将java字符串转换为字节数组并返回时,始终需要指示要使用的编码。如果省略此指示符,则只有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字符串转换为字节数组并返回总是需要指示要使用的编码。如果省略此指示符,则只有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类,并使用它们将数据转换为
字节[]
。之后,我通过了<代码>字节[]/COD>到C++。在服务器端,我通过JNI将C++代码层中的<代码>字节[]]/COD>发送到java服务器应用程序。Java服务器应用程序本身再次使用Java Protobuf类将
字节[]
解析为对象。我的Java源代码中没有
String
。这是可行的,但我仍然很好奇,是否有办法解决原始问题。

我无法解决原始问题。但我最终做的是生成Java Protobuf类,并使用它们将数据转换为
字节[]
。之后,我通过了<代码>字节[]/COD>到C++。在服务器端,我通过JNI将C++代码层中的<代码>字节[]]/COD>发送到java服务器应用程序。Java服务器应用程序本身再次使用Java Protobuf类将
字节[]
解析为对象。我的Java源代码中没有
String
。这是可行的,但我仍然很好奇,是否有办法解决最初的问题。

您可以使用,例如:

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.Builder msgBuilder = ToDoListMessageProto.newBuilder();
TextFormat.getParser().merge(sMsg, msgBuilder);
ToDoListMessageProto outputmessage = msgBuilder.build();
System.out.println("received message = " + outputmessage.toString());
例如,您可以使用:

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.Builder msgBuilder = ToDoListMessageProto.newBuilder();
TextFormat.getParser().merge(sMsg, msgBuilder);
ToDoListMessageProto outputmessage = msgBuilder.build();
System.out.println("received message = " + outputmessage.toString());

不要使用
String
,而只使用
byte[]
作为二进制数据。由于字符串在内部使用Unicode,您可以节省自己的编码和解码开销,这也很容易出错:不一定总是可能的。因为在C++端处理数组会要求内存管理。如果我把这样的数组从C++槽到JNI传递到java,我就不知道何时释放内存。这将导致大量额外的编程。只需对二进制数据使用
String
,而只使用
byte[]
。由于字符串在内部使用Unicode,您可以节省自己的编码和解码开销,这也很容易出错:不一定总是可能的。因为在C++端处理数组会要求内存管理。如果我把这样的数组从C++槽到JNI传递到java,我就不知道何时释放内存。这将导致大量额外的编程。protobuf的toByteArray方法似乎无法使用特定的编码。我只能获取一个“普通”字节数组。还有一个toByteString方法,但它似乎也不起作用。我将更新我的帖子。protobuf的toByteArray方法似乎不能使用特定的编码。我只能获取一个“普通”字节数组。还有一个toByteString方法,但它似乎也不起作用。我会更新我的帖子。