web-dev-qa-db-ja.com

C#リトルエンディアンまたはビッグエンディアン?

UDP/IPを介して制御できるハードウェアのドキュメントで、次のフラグメントが見つかりました。

この通信プロトコルでは、DWORDは4バイトデータ、Wordは2バイトデータ、BYTEはシングルバイトデータです。ストレージ形式はリトルエンディアンです。つまり、4バイト(32ビット)データは次のように格納されます。d7-d0、d15-d8、d23-d16、d31-d24。 2バイト(16ビット)データは、d7-d0、d15-d8として保存されます。

これがC#にどのように変換されるのだろうか?送信する前にコンテンツを変換する必要がありますか?たとえば、32ビット整数または4文字の文字列で送信したい場合は?

61
TimothyP

C#自体はエンディアンを定義しません。ただし、バイトに変換するときはいつでも選択を行っています。 BitConverter クラスには IsLittleEndian フィールドがあり、どのように動作するかを示しますが、選択肢はありません。 BinaryReader/BinaryWriterについても同じことが言えます。

My MiscUtil ライブラリには、エンディアンを定義できるEndianBitConverterクラスがあります。 BinaryReader/Writerにも同様の同等物があります。私は恐れているオンライン使用ガイドはありませんが、簡単です:)

(EndianBitConverterには、通常のBitConverterにはない機能があります。これは、バイト配列でインプレースで変換を行うことです。)

79
Jon Skeet

使用することもできます

IPAddress.NetworkToHostOrder(...)

短い、int、または長い。

45
Jan Bannister

リトルエンディアンについて、短い答えは(おそらく私は何でもする必要がある)「おそらくそうではないが、それはあなたのハードウェアに依存する」である。以下で確認できます:

bool le = BitConverter.IsLittleEndian;

これが何を言っているかに応じて、バッファの一部を逆にしたいかもしれません。あるいは、Jon Skeetには特定のエンディアンコンバーター here (EndianBitConverterを探します)があります。

イタニウム(たとえば)はビッグエンディアンです。ほとんどのIntelはリトルエンディアンです。

特定のUDP/IPについて...?

10
Marc Gravell

ネットワークのバイト順序とCPUのエンディアンネスについて知る必要があります。

通常、TCP/UDP通信では、 htons 関数(およびntohs、およびそれらに関連する関数)を使用して、常にデータをネットワークバイトオーダーに変換します。

通常、ネットワークの順序はビッグエンディアンですが、この場合(何らかの理由で!)commsはリトルエンディアンなので、これらの関数はあまり役に立ちません。あなたが実装したUDP通信が他の標準に従うと仮定することはできないため、これは重要です。また、ビッグエンディアンアーキテクチャを使用している場合、すべてをhtonsでラップできないため、生活が困難になります。 -(

ただし、Intel x86アーキテクチャを使用している場合は、すでにリトルエンディアンであるため、変換せずにデータを送信するだけです。

3
gbjbaanb

UDPマルチキャストのパックデータをいじっていますが、パケットヘッダー(Wireshark)のエラーに気付いたため、UInt16オクテットを並べ替える必要がありました。

    private UInt16 swapOctetsUInt16(UInt16 toSwap)
    {
        Int32 tmp = 0;
        tmp = toSwap >> 8;
        tmp = tmp | ((toSwap & 0xff) << 8);
        return (UInt16) tmp;
    }

UInt32の場合、

    private UInt32 swapOctetsUInt32(UInt32 toSwap)
    {
        UInt32 tmp = 0;
        tmp = toSwap >> 24;
        tmp = tmp | ((toSwap & 0xff0000) >> 8);
        tmp = tmp | ((toSwap & 0xff00) << 8);
        tmp = tmp | ((toSwap & 0xff) << 24);
        return tmp;
    }

これはテスト用です

    private void testSwap() {
        UInt16 tmp1 = 0x0a0b;
        UInt32 tmp2 = 0x0a0b0c0d;
        SoapHexBinary shb1 = new SoapHexBinary(BitConverter.GetBytes(tmp1));
        SoapHexBinary shb2 = new SoapHexBinary(BitConverter.GetBytes(swapOctetsUInt16(tmp1)));
        Debug.WriteLine("{0}", shb1.ToString());
        Debug.WriteLine("{0}", shb2.ToString());
        SoapHexBinary shb3 = new SoapHexBinary(BitConverter.GetBytes(tmp2));
        SoapHexBinary shb4 = new SoapHexBinary(BitConverter.GetBytes(swapOctetsUInt32(tmp2)));
        Debug.WriteLine("{0}", shb3.ToString());
        Debug.WriteLine("{0}", shb4.ToString());
    }

出力元は次のとおりです。

    0B0A: {0}
    0A0B: {0}
    0D0C0B0A: {0}
    0A0B0C0D: {0}
0
Marko Samirić

解析していてパフォーマンスが重要でない場合は、次の非常に単純なコードを検討してください。

private static byte[] NetworkToHostOrder (byte[] array, int offset, int length)
{
    return array.Skip (offset).Take (length).Reverse ().ToArray ();
}

int foo = BitConverter.ToInt64 (NetworkToHostOrder (queue, 14, 8), 0);
0
mafu