Reading byte array to type/variable in a simplified way?
I have a TCP socket connection where I get byte array of data which I have to decrypt and then format, so I am looking for a simple way to read this resulted byte array that I can easyly walk thru the elements exporting it as I need to a type or variable etc, to explain it better see the below example:
Let's say we have the byte array (packets are little-endian, this is just hypotetical example):
01 02 00 03 74 00 74 00 69 00
I want to put the first 2 bytes into an int, the next 2 bytes into another int and rest as an string.
So it would look like this translated:
int first = 258;
int seconnd = 3;
string rest = "tti"
I am not a java person, but on java there was something interesting I noticed somewhere of people reading packets directly like this:
messageSize = readH(); // for 2 bytes
key = readH(); // for 2 bytes
message = readS();开发者_运维知识库 // read as string
And I was wondering if there is similar pre-made feature in c# or any other resource that may help me better doing this task ?
You can use BinaryReader
like this:
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);
}
However, this will only work if little-endian is used.
The example you posted is big-endian.
I assume that you implement both writer and sender in C#, so you are good to go with BinaryReader
and BinaryWriter
, they both use little-endian, so they will understand each other.
[Edit]
Another approach you might want to consider is using struct
.
For example in your case :
[StructLayout(LayoutKind.Sequential, Pack = 1)]
public struct MyStruct
{
public ushort First;
public ushort Second;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 10)]
public string MyString;
}
The code would look like this
var myStruct = new MyStruct { First = 1, Second = 2, MyString = "asd" };
var bytes = StructToBytes(myStruct);
var myStruct1 = BytesToStruct<MyStruct>(bytes);
And two utility methods:
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);
}
}
For simple examples, you could do something like:
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;
}
...
}
which gives you access to:
int i = stream.ReadH();
etc. However, personally I would be writing a custom blahReader
(for your blah
), with an internal byte[]
buffer and tracking cursor. This allows must more efficient reading in most cases (even compared to a BufferedStream
).
As a side-note, your string looks to be UTF-16 and read to the EOF; reading to EOF is a pain as it doesn't really allow you to encode 2 strings. I would (instead) be adding (first) the length of the string, and I would be using UTF-8 unless there is a good chance of large character points. Then reading a string is a case of:
- read the length
- decode that much via
Encoding.*
As a final point - if you are mostly writing small integers, there are alternative encoding styles for integers that might be useful (allowing single-byte for small values, but still allowing full int
ranges to be expressed).
精彩评论