Добре, имах точно същото предвид, докато се опитвах лесно да анализирам отговорите от патентован сървър. Ето един опростен пример, съобразен с вашия конкретен случай.
Първо имате нужда от няколко разширения, за да направите това много по-лесно. Имайте предвид, че за да направите това, трябва да използвате .NET 3.5 или по-нова версия ИЛИ вижте отговора тук.
Сега, ето какво имам да работя за моя клас разширения:
public static class EndianExtensions {
/// <summary>
/// Convert the bytes to a structure in host-endian format (little-endian on PCs).
/// To use with big-endian data, reverse all of the data bytes and create a struct that is in the reverse order of the data.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="buffer">The buffer.</param>
/// <returns></returns>
public static T ToStructureHostEndian<T>(this byte[] buffer) where T : struct {
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
T stuff = (T) Marshal.PtrToStructure(handle.AddrOfPinnedObject(), typeof(T));
handle.Free();
return stuff;
}
/// <summary>
/// Converts the struct to a byte array in the endianness of this machine.
/// </summary>
/// <typeparam name="T"></typeparam>
/// <param name="structure">The structure.</param>
/// <returns></returns>
public static byte[] ToBytesHostEndian<T>(this T structure) where T : struct {
int size = Marshal.SizeOf(structure);
var buffer = new byte[size];
GCHandle handle = GCHandle.Alloc(buffer, GCHandleType.Pinned);
Marshal.StructureToPtr(structure, handle.AddrOfPinnedObject(), true);
handle.Free();
return buffer;
}
public static Dictionary<string, string> GetTypeNames<T>(this T structure) where T : struct {
var properties = typeof(T).GetFields();
var dict = new Dictionary<string, string>();
foreach (var fieldInfo in properties) {
string[] words = fieldInfo.Name.Split('_');
string friendlyName = words.Aggregate(string.Empty, (current, word) => current + string.Format("{0} ", word));
friendlyName = friendlyName.TrimEnd(' ');
dict.Add(fieldInfo.Name, friendlyName);
}
return dict;
}
}
(Имайте предвид, че част от горното е извадка от източници на CodeProject, всички от които са под CPOL лиценз)
Друго важно нещо, което трябва да се отбележи, е, че разширението GetTypeNames
може да се използва за получаване на приятелско име за вашите свойства, ако използвате CamelCaps и долна черта, където искате интервали.
Последната ключова част за осъществяването на тази работа (поне за моя конкретен случай) е да декларирате вашите структури в обратно. Това е така, защото моят сървър използва голям endianness. Може да искате да опитате със и без промяна на endianness - което работи за вас.
Така че, за да използвате това, ето какво трябва да направите:
- Декларирайте вашата структура. Тъй като трябваше да го поставя в big endian преди предаване, всички мои са в обратен ред:
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Foo {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string User_Name;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string Password;
};
[StructLayout(LayoutKind.Sequential, Pack = 1)]
struct Bar {
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string Password;
[MarshalAs(UnmanagedType.ByValTStr, SizeConst = 32)]
public string User_Name;
};
Горното предполага, че действителното съдържание на буферите за данни за изпращане и получаване е дефинирано по различен начин, така че във вашия случай просто ще трябва да дефинирате една от тези структури. Имайте предвид, че те са посочени в обратен ред; отново, това се дължи на факта, че трябваше да го предам във формат big-endian.
Сега всичко, което трябва да се направи, е да се създаде структурата за изпращане:
// buffer for storing our received bytes
var barBuf = new byte[64];
// struct that we're sending
var fuz = new Foo {
User_Name = "username",
Password = "password"
};
// get the byte equivalent of fuz
var fuzBytes = fuz.ToBytesHostEndian().Reverse().ToArray();
// simulates sock.send() and sock.receive()
// note that this does NOT simulate receiving big-endian data!!
fuzBytes.CopyTo(barBuf, 0);
// do the conversion from bytes to struct
barBuf = barBuf.Reverse().ToArray();
// change this to ToStructureHostEndian<Bar>() if receiving big endian
var baz = barBuf.ToStructureHostEndian<Foo>();
// get the property names, friendly and non-friendly
var bazDict = baz.GetTypeNames();
// change this to typeof(Bar) if receiving big endian
var bazProps = typeof(Foo).GetFields();
// loop through the properties array
foreach (var fieldInfo in bazProps) {
var propName = fieldInfo.Name;
// get the friendly name and value
var fieldName = bazDict[propName];
var value = fieldInfo.GetValue(baz);
// do what you want with the values
Console.WriteLine("{0,-15}:{1,10}", fieldName, value);
}
Важно е да се отбележи, че чрез симулиране на командите sock.Send()
и sock.Receive()
с помощта на CopyTo()
, това не води до голям масив в barBuf
. Модифицирах съответно кода, но в случай, че използвате това, за да получавате големи крайни данни, просто променете редовете, посочени в кода.
Надявам се това да помогне. Отне ми много време, за да разбера себе си, тъй като тази информация беше разпръсната в множество източници.
person
Forest Kunecke
schedule
23.05.2014