2016-03-22 17 views
0

クライアントからサーバーにデータを送信しようとしています。どちらのアプリケーションもjavaで書かれています。しかし、SWIGラッパーよりもC++で実装されたtlsレイヤーを使用しています。 tls層は、クライアントからの文字列を受け取り、それをサーバー側に送信し、Javaサーバーアプリケーションに通知します(そして文字列を渡します)。ただし、この文字列にはシリアルデータが含まれている必要があります。どういうわけか私はprotobufを使ってデータをシリアライズするのに苦労しています。私はToDoListMessageという名前のjava protobufクラスを使いたいと思います。いるProtobufは次のようになります。長い文字列のprotobufでの異常な動作

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

しかし、生成されたJavaクラスは、それが前にシリアライズデータ解析に失敗:

com.google.protobuf.InvalidProtocolBufferException:プロトコルメッセージ タグが無効なワイヤータイプを持っていたが。

現在、サーバーにデータを送信していません。ただ、シリアル化をテストし、クライアント側に一部を解析:私が試した何

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

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

は、メッセージは次のようになります

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エンコードされた文字列の代わりに、配列のように使用してみましたone:

String sMsg = new String(b); 

この場合、アプリはさらに奇妙な動作をします。短い「トークン」(例えば未満129ビット)の解析が動作しますが、長いトークンの失敗:com.google.protobuf.InvalidProtocolBufferException

: プロトコルメッセージを解析中に、入力が途中で予期せず終了 フィールドのこれは、入力が切り捨てられているか、または埋め込まれたメッセージが独自の長さを誤って報告した である可能性があります。

私は本当に理由を伝えることはできません。現在、トークンには数字のみが含まれています。

私はprotobufからシリアライズされた文字列をどのように得ることができますか?

この場合も、このテストにはtls転送はありません。現在、すべてのことがクライアント側で行われています。

更新:私はいるProtobufメッセージから直接バイト配列をフェッチするので

がエンコーディングを通過することは不可能です。私は、メッセージのtoByteString方法もある見つかりましたが、この延ByteStringにtoStringUtf8を使用すると、どちらも動作していないよう:

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

私は私が長いか短いトークンを使用した場合と異なる同じエラーメッセージを(取得します、上記を参照)

+0

単純に 'String'を使用せず、バイナリデータに' byte [] 'だけを使用してください。文字列はUnicodeを内部的に使用するため、エンコードおよびデコードのオーバーヘッドを節約できます。これはエラーが発生しやすくなります。 –

+0

それはそれほど簡単ではありません。 C++側の配列を扱うとメモリ管理が要求されるためです。私がC++トラフjniからjavaにそのような配列を渡す場合、私は単にいつメモリを解放するのか知りません。それは多くの追加プログラミングを引き起こすでしょう。 –

答えて

0

は、私は元の問題を解決することができませんでした。しかし最終的にはJava Protobufクラスを生成し、データをbyte[]に変換するために使用しました。その後、私はbyte[]をC++に渡しました。サーバー側では、C++ TLSレイヤーのbyte[]をJNI経由でJavaサーバーアプリケーションに送信します。 Javaサーバーアプリケーション自体は、Java Protobufクラスを再度使用して、byte[]をオブジェクトに対して解析します。 No Stringは私のJavaソースコードに関係しています。これは機能しますが、元の問題を解決する方法がある場合は、私はまだ不思議です。

2

Javaの文字列をバイト配列に変換して戻すには、常にどのエンコーディングを使用するかの指示が必要です。このインジケータを省略すると、java7:StandardCharsets.US_ASCIIが正しく変換されるため、7ビット文字(「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: öäü 
+0

protobufのtoByteArrayメソッドは、特定のエンコーディングを使用できないようです。私はちょうど '通常の'バイト配列をフェッチすることができます。 toByteStringメソッドもありますが、これはうまくいきません。私は私のポストを更新します。 –

関連する問題