cgy*_*per 18 .net c# sockets marshalling endianness
我有一个系统,远程代理发送序列化结构(来自嵌入式C系统)供我通过IP/UDP读取和存储.在某些情况下,我需要发回相同的结构类型.我以为我使用Marshal.PtrToStructure(接收)和Marshal.StructureToPtr(发送)进行了很好的设置.但是,一个小问题是网络大端整数需要转换为我的x86小端格式才能在本地使用.当我再次发送它们时,大端是可行的方式.
以下是有问题的功能:
private static T BytesToStruct<T>(ref byte[] rawData) where T: struct
{
T result = default(T);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) where T: struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}
return rawData;
}
Run Code Online (Sandbox Code Playgroud)
还有一个可以像这样使用的快速示例结构:
byte[] data = this.sock.Receive(ref this.ipep);
Request request = BytesToStruct<Request>(ref data);
Run Code Online (Sandbox Code Playgroud)
所讨论的结构如下所示:
[StructLayout(LayoutKind.Sequential, CharSet = CharSet.Ansi, Pack = 1)]
private struct Request
{
public byte type;
public short sequence;
[MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
public byte[] address;
}
Run Code Online (Sandbox Code Playgroud)
在编组结构时,我可以用什么(通用)方式交换字节序?我的需要是这个示例中本地存储的'request.sequence'应该是little-endian以便向用户显示.我不想以结构特定的方式交换字节序,因为它是一般问题.
我的第一个想法是使用Reflection,但我对这个功能并不是很熟悉.此外,我希望有一个更好的解决方案,有人可以指出我.提前致谢 :)
Ada*_*son 20
反思似乎是实现你所追求的唯一真正的方法.
我在下面汇总了一些代码.它创建一个名为的属性EndianAttribute
,可以在结构上的字段级别应用.我已经包含了这个属性的定义,它是相关的枚举,以及使用它所需的代码修改.
作为旁注,您不需要将其定义rawData
为ref
参数.
请注意,这确实需要使用C#3.0/.NET 3.5,因为我在执行工作的函数中使用LINQ和匿名类型.但是,如果没有这些功能,重写功能并不困难.
[AttributeUsage(AttributeTargets.Field)]
public class EndianAttribute : Attribute
{
public Endianness Endianness { get; private set; }
public EndianAttribute(Endianness endianness)
{
this.Endianness = endianness;
}
}
public enum Endianness
{
BigEndian,
LittleEndian
}
private static void RespectEndianness(Type type, byte[] data)
{
var fields = type.GetFields().Where(f => f.IsDefined(typeof(EndianAttribute), false))
.Select(f => new
{
Field = f,
Attribute = (EndianAttribute)f.GetCustomAttributes(typeof(EndianAttribute), false)[0],
Offset = Marshal.OffsetOf(type, f.Name).ToInt32()
}).ToList();
foreach (var field in fields)
{
if ((field.Attribute.Endianness == Endianness.BigEndian && BitConverter.IsLittleEndian) ||
(field.Attribute.Endianness == Endianness.LittleEndian && !BitConverter.IsLittleEndian))
{
Array.Reverse(data, field.Offset, Marshal.SizeOf(field.Field.FieldType));
}
}
}
private static T BytesToStruct<T>(byte[] rawData) where T : struct
{
T result = default(T);
RespectEndianness(typeof(T), rawData);
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
result = (T)Marshal.PtrToStructure(rawDataPtr, typeof(T));
}
finally
{
handle.Free();
}
return result;
}
private static byte[] StructToBytes<T>(T data) where T : struct
{
byte[] rawData = new byte[Marshal.SizeOf(data)];
GCHandle handle = GCHandle.Alloc(rawData, GCHandleType.Pinned);
try
{
IntPtr rawDataPtr = handle.AddrOfPinnedObject();
Marshal.StructureToPtr(data, rawDataPtr, false);
}
finally
{
handle.Free();
}
RespectEndianness(typeof(T), rawData);
return rawData;
}
Run Code Online (Sandbox Code Playgroud)