udpのパケットなど、ネットに流れているデータはネットワークバイトオーダ(ビックエンディアン)になっています。 それに対してx86系のシステムはリトルエンディアンです。 相互にやり取りするにはビックエンディアン⇔リトルエンディアンの変換が必要になります。
ネットワークバイトオーダ → クライアントマシンのバイトオーダの変換にはBitConverter.ToInt16などとIPAddress.NetworkToHostOrderを組み合わせるようです。 (BitConverterの代わりにMemoryStream+BinaryReaderを使うやり方もあり。) まず、エンディアンがあっていようがいまいがBitConverterで得たい型の変数に読み込み、その後NetworkToHostOrderで変換という流れです。
この変換をシフト演算を使った自前のコードで書いているのをたまに見ますが、それは環境依存になります。 NetworkToHostOrderを使ったら環境依存になりません。 (データとシステムのエンディアンが合っていたらNetworkToHostOrderは何もしない。 違っていたらNetworkToHostOrderは変換してくれる。) 性能的な問題が無いのならNetworkToHostOrderに任せた方が良いでしょう。
ちなみに、逆の変換は元の型の変数でIPAddress.HostToNetworkOrderしてからBitConverter.GetBytesをします。
ネットワークバイトオーダ → クライアントマシンのバイトオーダの変換コードだけ書いておきましょう。 byte配列からshortを読み込むには。
byte[] testBytes = new byte[] { (byte)0x80, (byte)0 }; short value1 = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(testBytes, 0));
ushortなど、符号なしの場合はキャストが必要になります。
ushort value2 = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(testBytes, 0));
ushort型のデータを読み込んでint型の変数に格納する場合など、変数のビット数が増えるときは注意が必要です。 このコードを実行すると。
int value3 = IPAddress.NetworkToHostOrder(BitConverter.ToInt16(testBytes, 0)); int value4 = (ushort)IPAddress.NetworkToHostOrder(BitConverter.ToInt16(testBytes, 0));
値は次のようになります。
value3 : -32768 ... 11111111 11111111 10000000 00000000b value4 : 32768 ... 00000000 00000000 10000000 00000000b
NetworkToHostOrder(short)の戻り値はshort型なので、明示的にushortにキャストしないと数字が変わってしまいます。 .netが動くシステムは負の数の表現に2の補数を使っているので、
- short型で10000000 00000000b → -32768
- ushort型で10000000 00000000b → 32768
となります。 value3のキャストの様子はそのまま、
- [short型で-32768] >>int型に暗黙キャスト>> [int型で-32768]
value4の場合は明示的なキャストと暗黙キャストが組み合わさって、
- [short型で-32768] >>ushort型に明示キャスト>> [ushort型で32768] >>int型に暗黙キャスト>> [int型で32768]
となります。