介绍

主要内容

大端和小端

大端(Big Endian)和小端(Little Endian)定义了数据在物理内存中的存储方式。例如,存储一个 long 类型的值:0X 12 34 56 78

  • 大端:最高有效字节存储在最小的内存地址。 在内存中,它的存储方式为:12 34 56 78(最小地址在左侧)

  • 小端:最高有效字节存储在最大的内存地址。 在内存中,它的存储方式为:78 56 34 12

由于数据存储是以字节为单位(每个地址可以存储一个字节),因此在大端和小端之间转换只需要反转字节顺序。

值得注意的是,诸如 shortintlong 这样的数据类型会受到字节序的影响。而字符串(string)的存储不受影响。此外,由于 char 只占用一个字节,所以也不受影响。

网络字节序

不同的机器可能使用不同的字节序定义。Intel X86 架构使用小端字节序,而 ARM 架构的 CPU 使用大端字节序。如何确保不同机器之间的数据传输不会产生错误呢?

UDP/TCP/IP 协议规定:将接收到的第一个字节视为最高有效字节,这意味着在数据传输时使用大端字节序。

C#中的数据流

C# 提供了 MemoryStreamBinaryWriterBinaryReader 作为读写字节流的工具。然而,值得注意的是,BinaryWriterBinaryReader 在读写操作时并不考虑网络字节序。与 Java 的 ByteBuffer 不同,后者在内部处理这一问题。

byte[] byteArray = new byte[] { 0x00, 0x00, 0x00, 0x01 };

using (MemoryStream msIn = new MemoryStream(byteArray))
using (BinaryReader brIn = new BinaryReader(msIn)){
    var result = brIn.ReadInt32();
    Console.Write(result);
}

// output: 16777216

在上面的代码中,byteArray 表示大端格式的 Int32 类型值 1。如果通过网络接收到一个 1,这就是我们得到的数据。但由于 BinaryReader 默认使用机器的存储格式(小端字节序),因此输出的是对 0x 01 00 00 00 的解释:16777216。

Int32 a = 1;
byte[] byteArray;

using (MemoryStream msIn = new MemoryStream(32))
using (BinaryWriter writer = new BinaryWriter(msIn, Encoding.UTF8)){
    writer.Write(a);
    byteArray = msIn.ToArray();
}

foreach (byte b in byteArray)
    Console.Write(b + " ");

// output: 1 0 0 0

同样,使用 BinaryWriter 将数字写入字节流时也会产生类似的问题。在上面的代码中,数字 1 以小端格式写入字节流。

long a = 1;
var ret = BitConverter.GetBytes(a);

foreach (var c in ret)
    Console.Write($"   {c}");
// output: 1 0 0 0 0 0 0 0

BitConverter 的行为方式相同。 为了解决这个问题,你可以使用 IPAddress.HostToNetworkOrder,或者手动使用 Array.Reverse 反转字节顺序。

参考资料

https://www.cnblogs.com/ArrozZhu/p/8384218.html

Tags: ,

Updated: