C#リトルエンディアンまたはビッグエンディアン?
-
03-07-2019 - |
質問
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文字の文字列で送信する場合はどうなりますか?
解決
C#自体は、エンディアンを定義しません。ただし、バイトに変換するたびに、選択を行っています。 BitConverter クラスには IsLittleEndian フィールドを使用して、動作を確認できますが、選択はできません。 BinaryReader / BinaryWriterについても同じことが言えます。
私の MiscUtil ライブラリには、エンディアンを定義できるEndianBitConverterクラスがあります。 BinaryReader / Writerにも同様の同等物があります。私は恐れているオンライン使用ガイドはありませんが、簡単です:)
(EndianBitConverterには、通常のBitConverterにはない機能があり、バイト配列でインプレースで変換を実行します。)
他のヒント
使用することもできます
IPAddress.NetworkToHostOrder(...)
short、int、またはlong。
リトルエンディアンで、短い答え(何かをする必要があります)は「おそらくそうではありませんが、ハードウェアに依存します」です。以下で確認できます:
bool le = BitConverter.IsLittleEndian;
これが何を言っているかに応じて、バッファの一部を逆にしたいかもしれません。または、Jon Skeetには特定のエンディアンコンバーターこちら(EndianBitConverterを探します)があります。 p>
イタニウム(たとえば)はビッグエンディアンです。ほとんどのIntelはリトルエンディアンです。
特定のUDP / IPを再......
ネットワークのバイト順序とCPUのエンディアンネスについて知る必要があります。
通常、TCP / UDPコミュニケーションでは、 htons
関数(および ntohs
、およびそれらに関連する関数)。
通常、ネットワークの順序はビッグエンディアンですが、この場合(何らかの理由で!)commsはリトルエンディアンなので、これらの関数はあまり役に立ちません。あなたが実装しているUDP通信が他の標準に従っていると想定できないため、これは重要です。また、あなたが htons
ですべてをラップすることはできないので、ビッグエンディアンアーキテクチャを持っている場合、人生を難しくしますする必要があります:-(
ただし、Intel x86アーキテクチャを使用している場合は、すでにリトルエンディアンであるため、変換せずにデータを送信してください。
解析していてパフォーマンスが重要でない場合は、次の非常に単純なコードを検討してください。
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);
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}