int 到 byte[] 网络一致性

int to byte[] consistency over network

我有一个到处都在使用的结构,我将它作为 byteArray 存储在硬盘上并发送到其他平台。

我过去常常通过获取结构的字符串版本并在序列化期间使用 getBytes(utf-8) 和 getString(utf-8) 来做到这一点。这样我想我避免了大小端问题?

然而,这是相当多的开销,我现在正在使用这个:

 public static explicit operator byte[] (Int3 self)
    {

        byte[] int3ByteArr = new byte[12];//4*3
        int x = self.x;
        int3ByteArr[0] = (byte)x;
        int3ByteArr[1] = (byte)(x >> 8);
        int3ByteArr[2] = (byte)(x >> 0x10);
        int3ByteArr[3] = (byte)(x >> 0x18);
        int y = self.y;
        int3ByteArr[4] = (byte)y;
        int3ByteArr[5] = (byte)(y >> 8);
        int3ByteArr[6] = (byte)(y >> 0x10);
        int3ByteArr[7] = (byte)(y >> 0x18);
        int z = self.z;
        int3ByteArr[8] = (byte)z;
        int3ByteArr[9] = (byte)(z >> 8);
        int3ByteArr[10] = (byte)(z >> 0x10);
        int3ByteArr[11] = (byte)(z >> 0x18);

        return int3ByteArr;
    }



public static explicit operator Int3(byte[] self)
        {

            int x = self[0] + (self[1] << 8) + (self[2] << 0x10) + (self[3] << 0x18);
            int y = self[4] + (self[5] << 8) + (self[6] << 0x10) + (self[7] << 0x18);
            int z = self[8] + (self[9] << 8) + (self[10] << 0x10) + (self[11] << 0x18);

            return new Int3(x, y, z);

        }

它对我来说效果很好,但我不太确定 little/big endian 是如何工作的。当其他机器收到我作为 bytearray 发送的 int 时,我还需要在这里处理一些事情以确保安全吗?

是的,你知道。随着字节顺序的改变,保留位顺序的序列化将 运行 陷入困境。

取int值385

在 bigendian 系统中,它将存储为

000000000000000110000001

将其解释为 littleendian 将其解读为 100000011000000000000000 并反向转换为 8486912

如果您使用 BitConverter class,将会有一本书 属性 要求系统的字节顺序。位转换器还可以为您生成位数组。

您必须根据序列化或反序列化系统的字节序决定使用字节序和反转字节数组。

description on MSDN其实已经很详细了。为简单起见,他们在这里使用 Array.Reverse。我不确定您为了进行位操作而强制转换 to/from 字节实际上是最快的转换方式,但这很容易进行基准测试。

您当前的方法不适用于您的应用程序 运行 在使用 Big-Endian 的系统上的情况。在这种情况下,您根本不需要重新排序。

你不需要自己反转字节数组
而且你不需要自己检查系统的字节顺序

静态方法IPAddress.HostToNetworkOrder 将整数转换为大端顺序的整数。

静态方法IPAddress.NetworkToHostOrder 将整数转换为您的系统使用命令的整数

这些方法将检查系统的字节序,并且do/or不会对整数重新排序。

要从整数中获取字节并返回使用 BitConverter

public struct ThreeIntegers
{
    public int One;
    public int Two;
    public int Three;
}

public static byte[] ToBytes(this ThreeIntegers value )
{
    byte[] bytes = new byte[12];
    byte[] bytesOne = IntegerToBytes(value.One);
    Buffer.BlockCopy(bytesOne, 0, bytes, 0, 4);

    byte[] bytesTwo = IntegerToBytes(value.Two);
    Buffer.BlockCopy(bytesTwo , 0, bytes, 4, 4);

    byte[] bytesThree = IntegerToBytes(value.Three);
    Buffer.BlockCopy(bytesThree , 0, bytes, 8, 4);

    return bytes;
} 

public static byte[] IntegerToBytes(int value)
{
    int reordered = IPAddress.HostToNetworkOrder(value);
    return BitConverter.GetBytes(reordered);
}

并从字节转换为结构

public static ThreeIntegers GetThreeIntegers(byte[] bytes)
{
    int rawValueOne = BitConverter.ToInt32(bytes, 0);
    int valueOne = IPAddress.NetworkToHostOrder(rawValueOne);

    int rawValueTwo = BitConverter.ToInt32(bytes, 4);
    int valueTwo = IPAddress.NetworkToHostOrder(rawValueTwo);

    int rawValueThree = BitConverter.ToInt32(bytes, 8);
    int valueThree = IPAddress.NetworkToHostOrder(rawValueThree);

    return new ThreeIntegers(valueOne, valueTwo, valueThree);
}

如果您将使用 BinaryReaderBinaryWriter 来保存和发送到另一个平台,那么可以放弃 BitConverter 和字节数组操作。

// BinaryWriter.Write have overload for Int32
public static void SaveThreeIntegers(ThreeIntegers value)
{
    using(var stream = CreateYourStream())
    using (var writer = new BinaryWriter(stream))
    {
        int reordredOne = IPAddress.HostToNetworkOrder(value.One);
        writer.Write(reorderedOne);

        int reordredTwo = IPAddress.HostToNetworkOrder(value.Two);
        writer.Write(reordredTwo);

        int reordredThree = IPAddress.HostToNetworkOrder(value.Three);
        writer.Write(reordredThree);
    }
}

读取值

public static ThreeIntegers LoadThreeIntegers()
{
    using(var stream = CreateYourStream())
    using (var writer = new BinaryReader(stream))
    {
        int rawValueOne = reader.ReadInt32();
        int valueOne = IPAddress.NetworkToHostOrder(rawValueOne);

        int rawValueTwo = reader.ReadInt32();
        int valueTwo = IPAddress.NetworkToHostOrder(rawValueTwo);

        int rawValueThree = reader.ReadInt32();
        int valueThree = IPAddress.NetworkToHostOrder(rawValueThree);
    }
}

当然你可以重构上面的方法并获得更清晰的解决方案。
或添加 BinaryWriterBinaryReader.

的扩展方法