以简化的方式将字节数组读入类型/变量?

时间:2011-06-07 04:34:55

标签: c# sockets bytearray tcpclient

我有一个TCP套接字连接,我得到数据的字节数组,我必须解密然后格式化,所以我正在寻找一种简单的方法来读取这个结果字节数组,我可以轻松地通过导出它的元素我需要一个类型或变量等,以便更好地解释下面的例子:

假设我们有字节数组(数据包是little-endian,这只是一个假设的例子)

01 02 00 03 74 00 74 00 69 00

我想将前2个字节放入int,接下来的2个字节放入另一个int并以字符串形式休息。

所以它看起来像这样:

int first = 258;
int seconnd = 3;
string rest = "tti"

我不是一个java人,但在java上有一些有趣的东西,我注意到有人在这里直接读取数据包:

messageSize = readH(); // for 2 bytes
key = readH(); // for 2 bytes
message = readS(); // read as string

我想知道c#或其他资源中是否有类似的预制功能可以帮助我更好地完成这项任务?

2 个答案:

答案 0 :(得分:2)

您可以像这样使用BinaryReader

using (var reader = new BinaryReader(stream))
{
    var first = reader.ReadUInt16();
    var second = reader.ReadUInt16();
    var stringBytes = reader.ReadBytes(6);
    var str = Encoding.Unicode.GetString(stringBytes);
}

但是,这只有在使用little-endian时才有效。

您发布的示例是big-endian。

我假设您在C#中实现了编写者和发件人,因此您最好使用BinaryReaderBinaryWriter,他们都使用little-endian,因此他们会相互理解。

<强> [编辑]

您可能需要考虑的另一种方法是使用struct 例如,在您的情况下:

[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
    public ushort First;
    public ushort Second;
    [MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
    public string MyString;
}

代码看起来像这样

var myStruct = new MyStruct { First = 1, Second = 2, MyString = "asd" };

var bytes = StructToBytes(myStruct);

var myStruct1 = BytesToStruct<MyStruct>(bytes);

两种实用方法:

public static T BytesToStruct<T>(byte[] bytes) where T : struct
{
    AssertUtilities.ArgumentNotNull(bytes, "bytes");

    var structSize = Marshal.SizeOf(typeof(T));
    var pointer = IntPtr.Zero;
    try
    {
        pointer = Marshal.AllocHGlobal(structSize);
        Marshal.Copy(bytes, 0, pointer, structSize);
        return (T)Marshal.PtrToStructure(pointer, typeof(T));
    }
    finally
    {
        if (pointer != IntPtr.Zero)
            Marshal.FreeHGlobal(pointer);
    }
}

public static byte[] StructToBytes<T>(T structObject) where T : struct
{
    var structSize = Marshal.SizeOf(typeof(T));
    var bytes = new byte[structSize];
    var pointer = IntPtr.Zero;
    try
    {
        pointer = Marshal.AllocHGlobal(structSize);
        Marshal.StructureToPtr(structObject, pointer, true);
        Marshal.Copy(pointer, bytes, 0, structSize);
        return bytes;
    }
    finally
    {
        if (pointer != IntPtr.Zero)
            Marshal.FreeHGlobal(pointer);
    }
}

答案 1 :(得分:1)

对于简单示例,您可以执行以下操作:

static class StreamHelpers
{
    public static int ReadH(this Stream stream)
    {
        int x = stream.ReadByte(), y = stream.ReadByte();
        if (x < 0 || y < 0) throw new EndOfStreamException();
        return (y << 8) | x;
    }
    ...

}

可让您访问:

int i = stream.ReadH();

等。但是,个人我会编写一个自定义blahReader(针对您的blah),内置byte[]缓冲区和跟踪光标。这允许必须在大多数情况下更有效地阅读(甚至与BufferedStream相比)。

作为旁注,你的字符串看起来是UTF-16并读取到EOF;读取EOF是一件痛苦的事情,因为它实际上不允许您对 2 字符串进行编码。我会(而不是)添加(首先)字符串的 length ,并且我将使用UTF-8,除非很有可能存在大字符点。然后读取字符串是一个例子:

  • 阅读篇幅
  • 通过Encoding.*
  • 解码了那么多

作为最后一点 - 如果你主要是编写小整数,那么对于整数可能有用的替代编码样式(允许小字符的单字节,但仍然允许完整{{1要表达的范围)。