【问题标题】:int to byte[] consistency over network网络上的 int 到 byte[] 一致性
【发布时间】:2016-11-17 20:00:40
【问题描述】:

我有一个结构可以在各处使用,我将它作为 byteArray 存储在 hd 上并发送到其他平台。

我曾经通过获取结构的字符串版本并在序列化过程中使用 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);

        }

它对我来说效果很好,但我不太确定小/大端的工作原理。当其他机器收到我作为字节数组发送的 int 时,我是否还需要在这里处理一些安全的事情?

【问题讨论】:

  • 使用IPAddress.HostToNetwork 方法。目前,如果您的软件将在 big-endian 系统上运行 - 您最终会通过网络发送 little-endian 字节。

标签: c# serialization endianness


【解决方案1】:

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

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

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

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

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

对于从整数中获取字节并返回使用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 的扩展方法。

【讨论】:

  • 感谢您的建议。但是我对每次转换分配 3 个额外字节数组的开销并不满意。如果我在 endcode 和 decode 上检查 if(bitconverter.islittleendian) 然后只是以相反的顺序移动字节,我也会安全吗? (每个 int 不是 0..3 而是 3..0)
  • @user3488765,如果您将使用BinaryReader/Writer 将值写入流,则根本不需要字节数组。我的回答的要点是IPAddress.HostToNetworkOrder 将以正确的顺序重新排列字节。
【解决方案2】:

是的,你知道。随着字节序的变化,您保留位顺序的序列化将遇到麻烦。

取int值385

在 bigendian 系统中,它将被存储为

000000000000000110000001

将其解释为 littleendian 会将其解读为 100000011000000000000000 并反向翻译为 8486912

如果您使用 BitConverter 类,则会有一个要求系统字节序的 book 属性。位转换器还可以为您生成位数组。

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

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

【讨论】:

  • 所以我可以检查一个系统是否是小端,什么时候是,.. 我做我上面做的,但不是从索引 0..3 而是 3..1?
  • 这听起来很合理,是的。但请记住对反向翻译进行相同的检查。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2018-08-06
  • 2012-03-17
  • 2020-06-12
  • 1970-01-01
  • 2010-10-30
相关资源
最近更新 更多