[SIZE=2][FONT=Courier New][[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]StructLayout[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#008080]LayoutKind[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New].Sequential)][/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]struct [/COLOR][/SIZE][SIZE=2][COLOR=#008080]myStruct
[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New]{[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] nField;[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] bField;[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]ushort[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] uField;[/FONT]
[FONT=Courier New]}[/FONT]
[/SIZE]
Расположение данных в языках .NET
Положим мне известен некий протокол взаимодействия в сети, я хочу написать программу используюшую этот протокол. Я исполюзую C#. Так вот, прежде всего неноходимо создать структуры отвечающие за тот или иной тип сообщения. Возникает проблема: поля структуры длиной более одного байта будут записываться в прямом или обратном порядке? Ну на x86 в обратном. Но вот если способ специфицировать порядок записи. Или возможно надо написать код на C++. Вот не знаю как поступить, может есть какие-то атрибуты?
Код:
bField находится по смещению 4. Т.е. с 5 байт. Шестой и седьмой байты заняты uField. Соответственно, первые четыре заняты nField. Вся структура занимает 7 байт. Чтобы узнать, сколько структура занимает в unmanaged-памяти байт, я использую такую функцию:
Код:
[FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]static [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] _sizeof<T>()[/FONT]
[FONT=Courier New]{[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff] return [/COLOR][/SIZE][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].SizeOf([/SIZE][SIZE=2][COLOR=#0000ff]typeof[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New](T));[/FONT]
[FONT=Courier New]}[/FONT]
[/SIZE]
[FONT=Courier New]{[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff] return [/COLOR][/SIZE][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].SizeOf([/SIZE][SIZE=2][COLOR=#0000ff]typeof[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New](T));[/FONT]
[FONT=Courier New]}[/FONT]
[/SIZE]
Можно воспользоваться ключевым словом sizeof в C#, но его можно использовать только в unsafe-контексте. Аттрибут StructLayout говорит среде CLR о том, как надо располагать поля структуры в памяти. Параметр LayoutKind.Sequential говорит о том, что поля располагаются в лексической последовательности, т.е. строго так как я указал. Если указать LayoutKind.Auto, то поля будут расположены так, чтобы не возникала фрагментация памяти, т.е. не так как я указал. Ещё есть LayoutKind.Explicit. С его помощью вообще можно самому задавать размер и смещение каждого поля в памяти. Например:
Код:
[SIZE=2][FONT=Courier New][[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]StructLayout[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#008080]LayoutKind[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New].Explicit)][/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]struct [/COLOR][/SIZE][SIZE=2][COLOR=#008080]myStruct
[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New]{[/FONT]
[FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](2)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] nField; [/SIZE][SIZE=2][COLOR=#008000]//оставляем в начале структуры 2 байта
[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](6)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] bField;[/FONT]
[FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](7)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]ushort[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] uField;[/FONT]
[FONT=Courier New]}[/FONT]
[/SIZE]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]struct [/COLOR][/SIZE][SIZE=2][COLOR=#008080]myStruct
[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New]{[/FONT]
[FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](2)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]int[/COLOR][/SIZE][SIZE=2] nField; [/SIZE][SIZE=2][COLOR=#008000]//оставляем в начале структуры 2 байта
[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](6)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]byte[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] bField;[/FONT]
[FONT=Courier New] [[/FONT][/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]FieldOffset[/COLOR][/SIZE][SIZE=2](7)] [/SIZE][SIZE=2][COLOR=#0000ff]public [/COLOR][/SIZE][SIZE=2][COLOR=#0000ff]ushort[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] uField;[/FONT]
[FONT=Courier New]}[/FONT]
[/SIZE]
Читать/писать из созданных в unamaged-памяти структур можно специальным классом Marshal. Вот маленький пример записи/чтения в структуру myStruct (первый вариант):
Код:
[FONT=Courier New][SIZE=2][COLOR=#008080]myStruct[/COLOR][/SIZE][SIZE=2] m = [/SIZE][SIZE=2][COLOR=#0000ff]default[/COLOR][/SIZE][SIZE=2]([/SIZE][SIZE=2][COLOR=#008080]myStruct[/COLOR][/SIZE][SIZE=2]); [/SIZE][SIZE=2][COLOR=#008000]//создаём пустую структуру
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]IntPtr[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] pMyStruct;[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].StructureToPtr(m, pMyStruct, [/SIZE][SIZE=2][COLOR=#0000ff]true[/COLOR][/SIZE][SIZE=2]); [/SIZE][SIZE=2][COLOR=#008000]//получаем управляемый указатель
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].WriteInt32(pMyStruct, 0, 45); [/SIZE][SIZE=2][COLOR=#008000]//записали в поле nField число 45
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].WriteByte(pMyStruct, 4, 12); [/SIZE][SIZE=2][COLOR=#008000]//записали в поле bField число 12
[/COLOR][/SIZE][/FONT]
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]IntPtr[/COLOR][/SIZE][/FONT][SIZE=2][FONT=Courier New] pMyStruct;[/FONT]
[/SIZE][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].StructureToPtr(m, pMyStruct, [/SIZE][SIZE=2][COLOR=#0000ff]true[/COLOR][/SIZE][SIZE=2]); [/SIZE][SIZE=2][COLOR=#008000]//получаем управляемый указатель
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].WriteInt32(pMyStruct, 0, 45); [/SIZE][SIZE=2][COLOR=#008000]//записали в поле nField число 45
[/COLOR][/SIZE][/FONT][FONT=Courier New][SIZE=2][COLOR=#008080]Marshal[/COLOR][/SIZE][SIZE=2].WriteByte(pMyStruct, 4, 12); [/SIZE][SIZE=2][COLOR=#008000]//записали в поле bField число 12
[/COLOR][/SIZE][/FONT]
В памяти массивы и структуры размещаются в прямом порядке (т.е. слева направо).
Описане стркктур (фу-ии смены порядка байт, не обязательно было импортировать, но я не знал на тот момент о существовании метода IPAddress.NetworkToHostOrder (Int32) тоже самое с ipaddr2str) :
Код:
public class Native
{
[DllImport("Ws2_32.dll", EntryPoint = "htons")]
public static extern UInt16 htons(UInt16 value);
[DllImport("Ws2_32.dll", EntryPoint = "ntohl")]
public static extern int ntohl(int value);
[DllImport("Ws2_32.dll", EntryPoint = "ntohs")]
public static extern ushort ntohs(ushort value);
public static string ipaddr2str(int value)
{
uint ipaddr = (uint)value;
uint[] addr_byte = new uint[4];
int i;
uint xbyte;
for (i = 0; i < 4; i++)
{
xbyte = ipaddr >> (i * 8);
xbyte = xbyte & (uint)0x000000FF;
addr_byte = xbyte;
}
return String.Format("{0}.{1}.{2}.{3}", addr_byte[3], addr_byte[2],
addr_byte[1], addr_byte[0]);
}
}
[StructLayout( LayoutKind.Sequential)]
public struct FlowHeader
{
public UInt16 Version;
public UInt16 Count;
public int SysUpTime;
public int UnixSecs;
public int UnixNSecs;
public int FlowSequence;
public char EngineType;
public char EngineId;
public UInt16 Reserved;
public void Convert()
{
this.Count = Native.htons(this.Count);
this.Version = Native.htons(this.Version);
this.Reserved = Native.htons(this.Reserved);
this.UnixNSecs = Native.ntohl(this.UnixNSecs);
this.UnixSecs = Native.ntohl(this.UnixSecs);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct FlowRecord
{
public int SrcAddr;
public int DstAddr;
public int NextHop;
public UInt16 Input;
public UInt16 Output;
public int dPkts;
public int dOctets;
public int First;
public int Last;
public UInt16 SrcPort;
public UInt16 DstPort;
public char pad1;
public char TcpFlags;
public char Prot;
public char Tos;
public UInt16 Src_as;
public UInt16 Dst_as;
public char SrcMask;
public char DstMask;
public UInt16 pad2;
public void Convert()
{
this.Input = Native.htons(this.Input);
this.Output = Native.htons(this.Output);
this.SrcPort = Native.htons(this.SrcPort);
this.DstPort = Native.htons(this.DstPort);
this.Src_as = Native.htons(this.Src_as);
this.Dst_as = Native.htons(this.Dst_as);
this.pad2 = Native.htons(this.pad2);
this.SrcAddr = Native.ntohl(this.SrcAddr);
this.DstAddr = Native.ntohl(this.DstAddr);
this.dOctets = Native.ntohl(this.dOctets);
this.First = Native.ntohl(this.First);
this.Last = Native.ntohl(this.Last);
}
}
{
[DllImport("Ws2_32.dll", EntryPoint = "htons")]
public static extern UInt16 htons(UInt16 value);
[DllImport("Ws2_32.dll", EntryPoint = "ntohl")]
public static extern int ntohl(int value);
[DllImport("Ws2_32.dll", EntryPoint = "ntohs")]
public static extern ushort ntohs(ushort value);
public static string ipaddr2str(int value)
{
uint ipaddr = (uint)value;
uint[] addr_byte = new uint[4];
int i;
uint xbyte;
for (i = 0; i < 4; i++)
{
xbyte = ipaddr >> (i * 8);
xbyte = xbyte & (uint)0x000000FF;
addr_byte = xbyte;
}
return String.Format("{0}.{1}.{2}.{3}", addr_byte[3], addr_byte[2],
addr_byte[1], addr_byte[0]);
}
}
[StructLayout( LayoutKind.Sequential)]
public struct FlowHeader
{
public UInt16 Version;
public UInt16 Count;
public int SysUpTime;
public int UnixSecs;
public int UnixNSecs;
public int FlowSequence;
public char EngineType;
public char EngineId;
public UInt16 Reserved;
public void Convert()
{
this.Count = Native.htons(this.Count);
this.Version = Native.htons(this.Version);
this.Reserved = Native.htons(this.Reserved);
this.UnixNSecs = Native.ntohl(this.UnixNSecs);
this.UnixSecs = Native.ntohl(this.UnixSecs);
}
}
[StructLayout(LayoutKind.Sequential)]
public struct FlowRecord
{
public int SrcAddr;
public int DstAddr;
public int NextHop;
public UInt16 Input;
public UInt16 Output;
public int dPkts;
public int dOctets;
public int First;
public int Last;
public UInt16 SrcPort;
public UInt16 DstPort;
public char pad1;
public char TcpFlags;
public char Prot;
public char Tos;
public UInt16 Src_as;
public UInt16 Dst_as;
public char SrcMask;
public char DstMask;
public UInt16 pad2;
public void Convert()
{
this.Input = Native.htons(this.Input);
this.Output = Native.htons(this.Output);
this.SrcPort = Native.htons(this.SrcPort);
this.DstPort = Native.htons(this.DstPort);
this.Src_as = Native.htons(this.Src_as);
this.Dst_as = Native.htons(this.Dst_as);
this.pad2 = Native.htons(this.pad2);
this.SrcAddr = Native.ntohl(this.SrcAddr);
this.DstAddr = Native.ntohl(this.DstAddr);
this.dOctets = Native.ntohl(this.dOctets);
this.First = Native.ntohl(this.First);
this.Last = Native.ntohl(this.Last);
}
}
И Вот собственно код метода, который и осуществляет сбор и маршалинг данных:
Код:
private void collectFunc()
{
socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint localEndPoint = new System.Net.IPEndPoint(IPAddress.Any, Convert.ToInt32(this.m_Port));
socket.Bind(localEndPoint);
byte[] buffer = new byte[3000];
int length = 0;
FlowHeader header = new FlowHeader();
FlowRecord[] records;
this.m_state = CollectorState.Collecting;
for (; ; )
{
length = socket.Receive(buffer);
if (length > 0)
{
IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FlowHeader)));
Marshal.Copy(buffer, 0, pointer, Marshal.SizeOf(typeof(FlowHeader)));
header = (FlowHeader)Marshal.PtrToStructure(pointer, typeof(FlowHeader));
header.Convert();
Marshal.FreeHGlobal(pointer);
if (length == Marshal.SizeOf(header) + Marshal.SizeOf(typeof(FlowRecord))*header.Count)
if (header.Version == 5)
{
records = new FlowRecord[header.Count];
for (int index = 0; index < header.Count; index++)
{
IntPtr record_pointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FlowRecord)));
int start_index = Marshal.SizeOf(header) + index*Marshal.SizeOf(typeof(FlowRecord));
Marshal.Copy(buffer, start_index, record_pointer, Marshal.SizeOf(typeof(FlowRecord)));
records[index] = (FlowRecord)Marshal.PtrToStructure(record_pointer, typeof(FlowRecord));
Marshal.FreeHGlobal(record_pointer);
records[index].Convert();
}
if (Collected != null)
Collected(this, new CollectedEventArgs(header, records));
}
}
}
}
{
socket = new System.Net.Sockets.Socket(AddressFamily.InterNetwork, SocketType.Dgram, ProtocolType.Udp);
IPEndPoint localEndPoint = new System.Net.IPEndPoint(IPAddress.Any, Convert.ToInt32(this.m_Port));
socket.Bind(localEndPoint);
byte[] buffer = new byte[3000];
int length = 0;
FlowHeader header = new FlowHeader();
FlowRecord[] records;
this.m_state = CollectorState.Collecting;
for (; ; )
{
length = socket.Receive(buffer);
if (length > 0)
{
IntPtr pointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FlowHeader)));
Marshal.Copy(buffer, 0, pointer, Marshal.SizeOf(typeof(FlowHeader)));
header = (FlowHeader)Marshal.PtrToStructure(pointer, typeof(FlowHeader));
header.Convert();
Marshal.FreeHGlobal(pointer);
if (length == Marshal.SizeOf(header) + Marshal.SizeOf(typeof(FlowRecord))*header.Count)
if (header.Version == 5)
{
records = new FlowRecord[header.Count];
for (int index = 0; index < header.Count; index++)
{
IntPtr record_pointer = Marshal.AllocHGlobal(Marshal.SizeOf(typeof(FlowRecord)));
int start_index = Marshal.SizeOf(header) + index*Marshal.SizeOf(typeof(FlowRecord));
Marshal.Copy(buffer, start_index, record_pointer, Marshal.SizeOf(typeof(FlowRecord)));
records[index] = (FlowRecord)Marshal.PtrToStructure(record_pointer, typeof(FlowRecord));
Marshal.FreeHGlobal(record_pointer);
records[index].Convert();
}
if (Collected != null)
Collected(this, new CollectedEventArgs(header, records));
}
}
}
}
PS: за оптимизацию кода не отвечаю :D
2 ЗАЗ: что за BB-код так форматирует C#-код?
Спасибо, достаточно было сказать StructLayout. Просто в пустоту искать очень трудно.