3

特定のヘッダーとデータ本文を期待するCアプリケーションとソケットを介して接続しようとしていますが、その形式に準拠しようとすると、次のような問題が発生します。

ヘッダー:
-パケット開始用に2バイト。
-パケットサイズとヘッダーを示す4バイト。
-パケットタイプの場合は1バイト。

本文:
-パラメータの量を示す4バイト。

パラメータごとに次の1つのブロック:
-パラメータの経度の場合は4バイト。
-メッセージコンテンツのXバイト。
-エスケープ文字の場合は1バイト。

サーバーが期待していることの例を次に示します。

[%%] [13 00 00 00] [0] [12 00 00 00] [0b 00 00 00] [OPERATIONPERFORMED \ 0]

私が抱えている問題は、PrintWriterを介して情報を送信しようとすると、もう一方の端が実際の値ではなくASCII文字として16進値を取得することです。私はすでにByteBuffer、DataOutputStream、およびいくつかのオプションを試しましたが、それでもそれらはその形式で、またはさらに奇妙な形で出てきています。

私が調査していることから、エンディアンが反転しているように見えるので、正しいバイトを取得しますが、順序が間違っています。

私が今試している実装は次のとおりです。

  DataOutputStream stream = new DataOutputStream(socket.getOutputStream());
    PrintWriter writer = new PrintWriter(socket.getOutputStream()); 

    BufferedReader in = new BufferedReader(new InputStreamReader(socket.getInputStream()));

    // Header
    stream.writeChar(37);
    stream.writeChar(37); 
    stream.writeInt(26);
    stream.writeChar('\\0');

    stream.writeInt(1);
    stream.writeInt(11);

    stream.writeUTF("SET_PROBLE\\0");


    stream.flush();

    StringBuilder sb = new StringBuilder();

    boolean state = true;

    while (state) {
        if (in.ready()) {
            sb.append((char) in.read());
        }
        else {
            if (sb.length() > 0) {
                state = false;
            }
        }
    }

    System.out.println(sb.toString().trim());

}

あなたは私を助けることができます?

よろしくお願いします!

4

1 に答える 1

2

もちろん、Tomaszは、OutputStreamを直接使用してバイトを送信することについては正しいです。これを拡張して、なぜこれが真実であるかを詳しく説明したいと思います。

サーバーが期待しているバイトを確認しましょう。まず、値37(ascii %%)がそれぞれ2バイト必要です。 DataOutputStream.writeCharはそれを提供しません。ドキュメントがそれを明確に述べているようにWrites a char to the underlying output stream as a 2-byte value, high byte first.

stream.writeChar(37);
stream.writeChar(37);

結果は4バイトになります:0 37 0 37(または00 25 00 25 hex)。同様に、いくつかの4バイト整数が必要です。サーバーのサンプルに基づいて、これらの整数はリトルエンディアンモード(下位バイトが最初)でシリアル化されることに注意してください。したがって、DataOutputStream.writeIntは、それが原因で役立つことはありませんWrites an int to the underlying output stream as four bytes, high byte first

stream.writeInt(26)

必要に応じて、4バイト-0 0 0 26(または00 00 00 1A hex)になります(1A 00 00 00)。あなたはその考えを理解します。これはうまくいきません。

DataOutputStreamを破棄し、OutputStreamを直接操作することにより、これらの問題を修正する方法を示すサンプルコードを次に示します。

public static void myWriteInt(OutputStream out, int value) throws IOException
{
    out.write(value % 0xff);
    out.write( (value >> 8) % 0xff);
    out.write((value >> 16) % 0xff);
    out.write((value >> 24) % 0xff);

}
public static void main(String[] args) throws Exception
{

    ByteArrayOutputStream out = new ByteArrayOutputStream();
    // Replace with
    // OutputStream out = socket.getOutputStream()

    out.write((byte) 37);
    out.write((byte) 37);

    myWriteInt(out, 26);
    out.write((byte) 0);

    myWriteInt(out, 1);
    myWriteInt(out, 11);

    out.write("SET_PROBLE\0".getBytes("UTF-8"));

    System.out.println(DatatypeConverter.printHexBinary(out.toByteArray()));

ソケットの処理を回避するために、私はByteArrayOutputStreamに書き込んでいるので、16進値を出力して、要件に一致することを示すことができます。ソケット出力ストリームに置き換えるだけです。

このコードは以下を出力します(わかりやすくするためにいくつかのスペースを追加しました)

2525 1A000000 00 01000000 0B000000 5345545F50524F424C4500

最後のチャンクはSET_PROBLEで、その後に0が続きます。

また、カスタムのリトルエンディアンintマーシャラー(myWriteInt)を含めましたが、リトルエンディアンモードに設定された一時的なByteBufferを使用するなど、他のアプローチもあります。

お役に立てれば。

于 2012-10-26T20:08:21.990 に答える