Справочник функций

Ваш аккаунт

Войти через: 
Забыли пароль?
Регистрация
Информацию о новых материалах можно получать и без регистрации:

Почтовая рассылка

Подписчиков: -1
Последний выпуск: 19.06.2015

Расположение данных в языках .NET

723
01 сентября 2006 года
Tommy
78 / / 13.10.2002
Здравствуйте.
Положим мне известен некий протокол взаимодействия в сети, я хочу написать программу используюшую этот протокол. Я исполюзую C#. Так вот, прежде всего неноходимо создать структуры отвечающие за тот или иной тип сообщения. Возникает проблема: поля структуры длиной более одного байта будут записываться в прямом или обратном порядке? Ну на x86 в обратном. Но вот если способ специфицировать порядок записи. Или возможно надо написать код на C++. Вот не знаю как поступить, может есть какие-то атрибуты?
273
02 сентября 2006 года
3A3-968M
1.2K / / 22.12.2005
В .NET есть возможность работать с unmanaged кусками памяти. Например, объявляю структуру:
 
Код:
[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]

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]

Можно воспользоваться ключевым словом 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]

Читать/писать из созданных в 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]

В памяти массивы и структуры размещаются в прямом порядке (т.е. слева направо).
713
02 сентября 2006 года
Ap0k
360 / / 13.03.2006
Могу привести пример реально рабочего окда, как раз таки под твою задачу (работа с сетью по определённому протоколу, в частности NetFlow 5.0).
Описане стркктур (фу-ии смены порядка байт, не обязательно было импортировать, но я не знал на тот момент о существовании метода 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);
        }
    }


И Вот собственно код метода, который и осуществляет сбор и маршалинг данных:
Код:
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));
                    }
                }
            }
        }


PS: за оптимизацию кода не отвечаю :D
2 ЗАЗ: что за BB-код так форматирует C#-код?
723
02 сентября 2006 года
Tommy
78 / / 13.10.2002
Спасибо, достаточно было сказать StructLayout. Просто в пустоту искать очень трудно.
Реклама на сайте | Обмен ссылками | Ссылки | Экспорт (RSS) | Контакты
Добавить статью | Добавить исходник | Добавить хостинг-провайдера | Добавить сайт в каталог