4

Windows サーバーで実行されているC プログラムと通信するアプリを開発しています。このプログラムは Visual Studio を使用して開発されています (この情報が役立つ場合)。

サーバーはソケット通信を介して整数を送信します. サーバーを送信する前に、次のことを行います:-

  1. int を宣言する
  2. それに何らかの値を割り当てます
  3. memcpyを使用して、2 バイトを char * (バッファなど) にコピーします。
  4. そのバッファにさらにデータを追加します
  5. そのバッファを送信します

受信側でJavaを実装しているため、memcpyを直接使用することはできません。使用しました

short mId = java.nio.ByteBuffer.wrap(recvBuf, 0, 2).order(ByteOrder.LITTLE_ENDIAN).getShort();

これは問題なく機能しますが、コードのこの部分は数ミリ秒ごとに呼び出されるため、最適化しようとしています..私も使用しました

short mId =(short)(recvBuf[0] + recvBuf[1]*128);

これも問題なく機能しますが、将来数が増えた場合に機能するかどうかは疑問です. javaでmemcpyの繰り返しを行う最良の方法は何ですか?

私はこのスレッドにアクセスしましたが、それはあまり役に立ちません。

編集 私は私のために働いた次の4つの方法を実装しました、

public class CommonMethods {
    /*
     * Returns the byte[] representation of an int in Little Endian format
     * 
     * @param value that should be converted to byte[]
     */
    public static byte[] toByteArray(int value) {
        return new byte[] { (byte) value, (byte) (value >> 8), (byte) (value >> 16), (byte) (value >> 24) };
    }

    /*
     * Returns the int in LittleEndian value of the passed byte[]
     * 
     * @param bytes is the input byte[]
     * 
     * @param offset is the offset to start
     */
    public static int getInt(byte[] bytes, int offset, int length) {
        int retValue = (bytes[offset] & 0xFF);
        byte bVal;
        for (int i = 1; i < length; i++) {
            bVal = bytes[offset + i];
            retValue |= ((bVal & 0xFF) << (8 + (8 * (i - 1))));
        }
        return retValue;
    }

    /*
     * Returns the int in BigEndian from the passed byte[]
     * 
     * @param bytes is the byte[]
     */
    public static int getIntBigEndian(byte[] bytes, int offset, int length) {
        int retValue = (bytes[offset + length - 1] & 0xFF);
        for (int i = 1; i < length; i++) {
            retValue |= ((bytes[offset + length - 1 - i] & 0xFF) << (8 + (8 * (i - 1))));
        }
        return retValue;
    }

    /*
     * Returns the byte[] representation of an int in Big Endian format
     * 
     * @param value that should be converted to byte[]
     */
    public static byte[] toByteArrayBigEndian(int value) {
        return new byte[] { (byte) (value >> 24), (byte) (value >> 16), (byte) (value >> 8), (byte) value };
    }
}
4

2 に答える 2

3

直接受け取ることができますByteBuffer。これにより、いくつかのシステム コールやメモリ コピーが節約されます。

ByteBuffer buf = ByteBuffer.allocate(1024); // just allocate enough, can be reused later;
buf.reset();  // in case this is reused, you need reset()

SocketChannel channel = socket.getChannel();
channel.read(buf);

short mId = buf.order(ByteOrder.LITTLE_ENDIAN).getShort();

byte[] otherBytes = new byte[....];
buf.get(otherBytes);

もちろん、読み取った実際の長さを確認する必要があります。わかりやすくするために、それらを省略しました。詳細を知りたい場合は、「Java nio ブロッキング モード」をググってください。

于 2012-06-21T13:51:41.900 に答える
1

このコード:

short mId =(short)(recvBuf[0] + recvBuf[1]*128);

間違いなく正しくありません。試した値によってはうまくいくかもしれませんが、他の値ではうまくいかないでしょう。

recvBuf[1]が 0 以外の場合、正しくない結果が得られます。

recvBuf[0]が 127 より大きい場合、不正確な結果が得られます。

理由は次のとおりです。

  • byteJava では署名されています。バイトの値が 128 から 255 の間の場合、負の数として解釈されます (これはおそらくあなたが望むものではありません)。
  • recvBuf[1]128 を掛けるのは正しくありません。値を正しくスケールアップするには、256 を掛ける必要があります。負の数に関する上記のポイントも参照してください

C からのバイトを LITTLE-ENDIAN バイト順で書き込んでいると仮定すると、代わりに次のようにすることができます。

int mId = ((recvBuf[0] & 0xFF) | ((recvBuf[1] & 0xFF) << 8));

& 0xFF、上位ビットが設定されている場合に、値の下位 8 ビットのみをマスクするために使用されます (Java は int に変換するときにバイトを符号拡張するため)。

<< 8と機能的に同等ですが* 256、算術関数ではなくビット シフト関数であり、この使用例により適しています。

int結果をa の代わりにan に代入するshortと、32768 ~ 65535 の値が負の数ではなく正の数として正しく解釈されることが保証されます。

これは、0 から 65535 までのすべての値で機能します。数値がそれよりも大きくなると、とにかく送信するときに 2 バイトに入れることができなくなります。将来の計画を立て、2 バイト以上が必要になる可能性があると思われる場合は、C 整数の 4 バイトすべてをコピーし、次のように Java で値を読み取ります。

long mId = (long)(((recvBuf[0] & 0xFF) | ((recvBuf[1] & 0xFF) << 8) |
              ((recvBuf[2] & 0xFF) << 16) | ((recvBuf[3] & 0xFF) << 24)) & 0xFFFFFFFF);

また、Java のデフォルトのバイト順は LITTLE-ENDIAN ではなく BIG-ENDIAN であることにも注意してください。

于 2012-06-21T13:57:42.397 に答える