Форум программистов, компьютерный форум, киберфорум
C# для начинающих
Войти
Регистрация
Восстановить пароль
Блоги Сообщество Поиск Заказать работу  
 
Рейтинг 4.88/76: Рейтинг темы: голосов - 76, средняя оценка - 4.88
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148

Преобразование класса в массив байт

19.07.2013, 07:18. Показов 14612. Ответов 15
Метки нет (Все метки)

Студворк — интернет-сервис помощи студентам
Доброго времени суток!
Задача следующая. Через сокет приходит пакет данных в виде байтового массива (datagrab). На серверном приложении до преобразования, пакет существует в виде класса аналог которого на C# будет выглядеть примерно так:
C#
1
2
3
4
5
6
7
8
9
10
public class paket
    {
        public byte type = 0;
        public byte cmd = 0;
        public byte len = 0;
        public byte res = 0;
        public char[] login = new char[63];
        public double datetime;
        public char[] hash = new char[20];
    }
Необходимо преобразовать массив байт в формат такого класса, совершить над ним некие операции (допустим изменить поля), класс преобразовать в массив байт и отправить ответ серверу.
У меня проблема с преобразованием. Просто один тип преобразовать проблем не возникает, а вот с классом не понятно как дело обстоит.
За ранее благодарю.
0
IT_Exp
Эксперт
34794 / 4073 / 2104
Регистрация: 17.06.2006
Сообщений: 32,602
Блог
19.07.2013, 07:18
Ответы с готовыми решениями:

Преобразование изображения в массив байт
Уважаемые коллеги, подскажите как из изображения(тип любой) получить массив байт? Слышал, что можно через Encoding, а ещё как? Рассмотрю...

Преобразование уинт\инт числа в байт массив
Здравствуйте. Необходимо преобразовать uint\int числа в байт массив БЕЗ использования BitConvertor-а. Набросал код с использованием бит...

Преобразование MD5 в массив байт
Привет, MD5 это 128 битный хеш. К примеру MD5("key") = "3c6e0b8a9c15224a8228b9a98ca1531d" как это перевести в 16 байт? Понятно, что это...

15
 Аватар для vialet
57 / 57 / 11
Регистрация: 04.03.2010
Сообщений: 244
19.07.2013, 08:16
использовать сериализацию, для этого пометить класс атрибутом
C#
1
[Serializable()]
1
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148
19.07.2013, 09:24  [ТС]
Цитата Сообщение от vialet Посмотреть сообщение
использовать сериализацию, для этого пометить класс атрибутом
C#
1
[Serializable()]
Вопрос в том, подойдет ли сериализация в данном случае? Проблема в том что сервер принимает пакет определенного количества байт. На сколько мне известно, при сериализации размер пакета в байтах будет больше чем реально занимаемое пространство.
Есть вариант сделать не класс, а структуру и явно указать размер выделяемой памяти. Вот так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[StructLayout(LayoutKind.Explicit)]
        public struct st
        {
            [FieldOffset(0)]
            public byte type; //1 byte
            [FieldOffset(1)]
            public byte cmd; //1 byte
            [FieldOffset(2)]
            public byte len; //1 byte
            [FieldOffset(3)]
            public byte res; //1 byte
            [FieldOffset(4)]
            public char[] login; //64 byte
            [FieldOffset(68)]
            public double datetime; //8 byte
            [FieldOffset(76)]
            public char[] hash;//21 byte
        }
Но тут всплывает проблема что в структуре нельзя инициализировать типы, а у меня поле hash должно быть 21 байт. В данном случае пакет получается 80 байт, так как последнее поле выравнивается к формату 4 байта. Порядок полей я так же изменить не могу, потому-что сервер не сможет правильно прочитать пакет данных.

Во избежание лишних вопросов, не я придумал такой пакет. Мне просто нужно следовать установленному формату.

Добавлено через 6 минут
у меня пакет данных должен быть размером в 97 байт.

Добавлено через 20 минут
Проблема решилась вот так:
C#
1
2
[FieldOffset(76), MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
public char[] hash;//21 byte
Осталось теперь только перевести в массив байт чтобы размер не изменился. Сериализация не подходит. Если кто знает как это реализовать вручную, то прошу дать пример.

Добавлено через 11 минут
Есть мысль что можно каждое поле по отдельности конвертировать с помощью BitConverter.GetBytes, а потом собрать все результаты в один байтовый массив как-будто класс полностью преобразовали. Но это в теории, как это реализовать я что-то даже не представляю. Может кто знает как сделать это или проще способ?
0
 Аватар для Евгений В
912 / 672 / 134
Регистрация: 01.03.2010
Сообщений: 1,279
19.07.2013, 09:33
natrox,
Здесь была похожая тема
как сохранить в массиве байт разнотипную информацию, а потом извлечь ее
1
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148
19.07.2013, 11:45  [ТС]
Цитата Сообщение от Евгений В Посмотреть сообщение
Попытка сделать таким методом успеха не принесла к сожалению

Структура:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
[StructLayout(LayoutKind.Explicit, Size=96)]
        public struct st
        {
            [FieldOffset(0)]
            public byte type; //1 byte
            [FieldOffset(1)]
            public byte cmd; //1 byte
            [FieldOffset(2)]
            public byte len; //1 byte
            [FieldOffset(3)]
            public byte res; //1 byte
            [FieldOffset(4)]
            public char[] login; //64 byte
            [FieldOffset(68)]
            public double datetime; //8 byte
            [FieldOffset(76), MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public char[] hash;//20 byte
        }
Вот я пробую преобразовать в массив байт, но размеры почему-то меняются. Может кто объяснит почему так происходит?
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
public byte[] DatagrabToArray()
        {
            byte[] bytes1 = BitConverter.GetBytes(datagrab.type);
            byte[] bytes2 = BitConverter.GetBytes(datagrab.cmd);
            byte[] bytes3 = BitConverter.GetBytes(datagrab.len);
            byte[] bytes4 = BitConverter.GetBytes(datagrab.res);
            byte[] bytes5 = Encoding.Default.GetBytes(datagrab.login);
            byte[] bytes6 = BitConverter.GetBytes(datagrab.datetime);
            byte[] bytes7 = Encoding.Default.GetBytes(datagrab.hash);
 
            Stream ms = new MemoryStream();
            ms.Write(bytes1, 0, bytes1.Length);
            ms.Write(bytes2, 0, bytes2.Length);
            ms.Write(bytes3, 0, bytes3.Length);
            ms.Write(bytes4, 0, bytes4.Length);
            ms.Write(bytes5, 0, bytes5.Length);
            ms.Write(bytes6, 0, bytes6.Length);
            ms.Write(bytes7, 0, bytes7.Length);
 
            richTextBox1.AppendText(bytes1.Length.ToString() + " \n");//2
            richTextBox1.AppendText(bytes2.Length.ToString() + " \n");//2
            richTextBox1.AppendText(bytes3.Length.ToString() + " \n");//2
            richTextBox1.AppendText(bytes4.Length.ToString() + " \n");//2
            richTextBox1.AppendText(bytes5.Length.ToString() + " \n");//8
            richTextBox1.AppendText(bytes6.Length.ToString() + " \n");//8
            richTextBox1.AppendText(bytes7.Length.ToString() + " \n");//40
 
            ms.Seek(0, System.IO.SeekOrigin.Begin);
 
            byte[] mas = new byte[ms.Length];
            ms.Read(mas, 0, Convert.ToInt32(ms.Length));
 
            return mas;
        }
Т.е. такой массив мне уже не подойдет, по той причине что сервер не сможет его правильно распознать.

Добавлено через 40 минут
BitConverter.GetBytes возвращает 16 битовое значение. Поэтому получается не 1, а 2 байта на выходе. А мне надо чтобы остался 1 байт как в структуре.
0
 Аватар для MasMaX
8 / 8 / 2
Регистрация: 07.02.2012
Сообщений: 71
19.07.2013, 13:31
Цитата Сообщение от natrox Посмотреть сообщение
Вопрос в том, подойдет ли сериализация в данном случае? Проблема в том что сервер принимает пакет определенного количества байт. На сколько мне известно, при сериализации размер пакета в байтах будет больше чем реально занимаемое пространство.
Можно применить сериализацию, и получить на выходе файл. А потом этот файл уже по пакетам (в ручную разбить на любое количество байт) передавать на сервер.
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
19.07.2013, 20:14
Мб выравнивание?

Добавлено через 2 минуты
Попробуйте так:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
    [StructLayout(LayoutKind.Explicit, Size = 96, Pack = 1)]
    public struct Struct
    {
        [FieldOffset(0)]
        public byte type; //1 byte
        [FieldOffset(1)]
        public byte cmd; //1 byte
        [FieldOffset(2)]
        public byte len; //1 byte
        [FieldOffset(3)]
        public byte res; //1 byte
        [FieldOffset(4)]
        public char[] login; //64 byte
        [FieldOffset(68)]
        public double datetime; //8 byte
        [FieldOffset(76), MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
        public char[] hash;//20 byte
    }
0
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
19.07.2013, 21:28
Сериализация не пройдет. потому что там отправляются метаданные.
Если вы хотите "кидатся чистыми данными" вам надо структуру привести в массив байт.
Как это сделать?
* есть несколько способов, самым быстрым является связка Marshal + unsafe код.

C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
        unsafe byte[] GetBytes<T>(T obj) where T : struct
        {
            var size   = Marshal.SizeOf(typeof(T));
            var buffer = new byte[size];
 
            fixed (void* pointer = buffer)
            {
                Marshal.StructureToPtr(obj, new IntPtr(pointer), false);
                return buffer;
            }
        }
 
        unsafe T CreateStruct<T>(byte[] buffer) where T : struct
        {
            fixed (void* pointer = buffer)
            {
                return (T)Marshal.PtrToStructure(new IntPtr(pointer), typeof(T));
            }
        }
Добавлено через 4 минуты
Рабочий пример:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
using System;
using System.Runtime.InteropServices;
 
namespace FastStructWriter
{
    public struct MyStruct
    {
        public int f1;
        public int f2;
        public int f3;
        [MarshalAs(UnmanagedType.ByValArray, SizeConst = 5)]
        public float[] f4;
    }    
    class Program
    {
        static unsafe void Main(string[] args)
        {
            var ms = new MyStruct() { f1 = 5, f2 = 6, f3 = 7, f4 = new float[] { 43.5f, 47.5f, 55.5f, 45.85f, 95.8f } };
 
            var b = GetBytes<MyStruct>(ms);
            var m = CreateStruct<MyStruct>(b);
 
            Console.ReadLine();
        }
 
        static unsafe byte[] GetBytes<T>(T obj) where T : struct
        {
            var size   = Marshal.SizeOf(typeof(T));
            var buffer = new byte[size];
 
            fixed (void* pointer = buffer)
            {
                Marshal.StructureToPtr(obj, new IntPtr(pointer), false);
                return buffer;
            }
        }
 
        static unsafe T CreateStruct<T>(byte[] buffer)
        {
            fixed (void* pointer = buffer)
            {
                return (T)Marshal.PtrToStructure(new IntPtr(pointer), typeof(T));
            }
        }    
    }
}
ЗЫ. Не забывайте sizeof(char) == 2
2
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148
20.07.2013, 08:09  [ТС]
Цитата Сообщение от MasMaX Посмотреть сообщение
Можно применить сериализацию, и получить на выходе файл. А потом этот файл уже по пакетам (в ручную разбить на любое количество байт) передавать на сервер.
Нет, сериализация тут не подходит по той причине что я десериализовать не смогу на сервере пакет. Серверное приложение мне не доступно, я лишь могу ему отправить определенный пакет и получить от него ответ в виде определенного пакета данных.

Добавлено через 3 минуты
Konctantin, огромное спасибо за совет. Попробую сделать так как вы посоветовали. Думаю мне это подойдет.
0
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148
22.07.2013, 09:32  [ТС]
Konctantin, попытался применить ваш метод и получил ошибку:
"Тип указанного массива не совпадает с ожидаемым."

на вот эту строку
C#
1
Marshal.StructureToPtr(obj, new IntPtr(pointer), false);
почему так получается? поля в структуре не сосуществуют типу который может преобразовать Marshal или почему так получилось?

Добавлено через 17 часов 51 минуту
А с помощью BinaryWriter есть вариант это сделать? Даже если каждое поле в отдельности записывать в массив байт придется, мне такое тоже подойдет. Уже любой вариант подойдет, лишь бы работало

Добавлено через 58 секунд
Если кто может дать пример преобразования моей структуры в массив байт в ручную, буду очень благодарен

Добавлено через 4 часа 0 минут
Ситуация следующая:

Ошибка была по той причине что у меня в структуре поле логин помечено размером в 64 байта, а на деле я в него записываю 8 байт. Проблема в том что поле должно быть 64 байта, сервер ждет именно такой размер. Но допустим логин на 8 символов, да и вообще не известно какой там логин будет у пользователя, 64 байта это размер с запасом на любой логин

Что можно сделать в таком случае?

Код структуры на данный момент:
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
        [StructLayout(LayoutKind.Explicit, Pack=1)]
        public struct st
        {
            [FieldOffset(0)]
            public byte type; //1 byte
            [FieldOffset(1)]
            public byte cmd; //1 byte
            [FieldOffset(2)]
            public byte len; //1 byte
            [FieldOffset(3)]
            public byte res; //1 byte
            [FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
            public char[] login; //64 byte
            [FieldOffset(68)]
            public double datetime; //8 byte
            [FieldOffset(76), MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public char[] hash;//20 byte
        }
0
Master of Orion
Эксперт .NET
 Аватар для Psilon
6102 / 4958 / 905
Регистрация: 10.07.2011
Сообщений: 14,522
Записей в блоге: 5
22.07.2013, 11:19
C#
1
2
3
4
            string login = "mylogin";
 
            var struct64Fielld = new char[64];
            Array.Copy(login.ToCharArray(), struct64Fielld, login.Length);
Добавлено через 2 минуты
C#
1
2
3
            string login = "mylogin";
 
            string struct64Fielld = new string((char) 0, 64).Insert(0, login);
еще много способов.
1
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
22.07.2013, 11:24
1) не заморачивайтесь с оффсетами, они тут вам не нужны, достаточно 1 атритута
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
[StructLayout(LayoutKind.Sequential)]
public struct st
{
    public byte type;
    public byte cmd;
    public byte len;
    public byte res;
    [MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
    public char[] login;
    public double datetime;
    [ MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
    public char[] hash;
 
    public string Login
    {
        get { return string.Join("", login.TakeWhile(n => n != 0)); }
        set { login = value.PadRight(64, '\0').ToArray(); }
    }
}
2) вы можете дописывать нулями незанятое место.
Но так как это не практично, то вам надо сделать свой обработчик и построитель пакетов.
2
22.07.2013, 11:29

Не по теме:

Konctantin, чет я тоже про свойство подумал :)

0
 Аватар для Konctantin
970 / 773 / 171
Регистрация: 12.04.2009
Сообщений: 1,700
22.07.2013, 11:51
вобще-то забивать массив нулями не есть хорошо, это же лишний размер.
Я бы поступил так:
Создавал бы структуру, в начале писал бы нативные типы, а в конец засунул бы строки.
Далее, ограничивал бы структуру размером нативных типов, а строки дописывал бы в конец пакета.
В место строк указал бы их смещения в пакете.
при разборе обратно, получили нативные типы, а потом прочитал бы строки из оставшегося куска.

В реализации немного сложнее, но суть остается та же.

Вот только при реализации "сложных" пакетов такой фокус не получится.
1
 Аватар для Anklav
447 / 305 / 47
Регистрация: 23.01.2013
Сообщений: 661
22.07.2013, 12:28
C#
1
2
3
4
5
6
7
8
9
10
11
12
13
14
            [FieldOffset(0)]
            public byte type; //1 byte
            [FieldOffset(1)]
            public byte cmd; //1 byte
            [FieldOffset(2)]
            public byte len; //1 byte
            [FieldOffset(3)]
            public byte res; //1 byte
            [FieldOffset(4), MarshalAs(UnmanagedType.ByValArray, SizeConst = 64)]
            public char[] login; //64 byte
            [FieldOffset(68)]
            public double datetime; //8 byte
            [FieldOffset(76), MarshalAs(UnmanagedType.ByValArray, SizeConst = 20)]
            public char[] hash;//20 byte
Вам же писали, char занимает 2 байта, а SizeConst это длинна не в байтах, а в количестве элементов, в итоге получается наезд данных друг на друга. Вместо char[] укажите byte[], и выполняйте преобразование с помощью класса Encoding в массив байт.
1
 Аватар для natrox
14 / 18 / 5
Регистрация: 13.04.2011
Сообщений: 148
23.07.2013, 04:55  [ТС]
Проблема решилась благодаря добавлению пустых значений в конец строки.
C#
1
2
3
login = st_fr.textBox2.Text;
                login = string.Join("", login.TakeWhile(n => n != 0)); 
                login = login.PadRight(64, '\0');
0
Надоела реклама? Зарегистрируйтесь и она исчезнет полностью.
BasicMan
Эксперт
29316 / 5623 / 2384
Регистрация: 17.02.2009
Сообщений: 30,364
Блог
23.07.2013, 04:55
Помогаю со студенческими работами здесь

Преобразование HICON в массив байт и обратно
Всем доброго времени суток! Пишу 2 приложения: Приложение 1, не mfc: Вытаскивает из .exe ресурс иконки HICON, преобразует в...

Дан массив из 11 байт. Подсчитать количество байт с нечетным числом 1 в байте
Дан массив из 11 байт. Подсчитать количество байт с нечетным числом 1 в байте.-Assembler помогите пожалуйста

Дан массив из 10 байт. Посчитать количество байт, в которых сброшены 6 и 4 биты
Дан массив из 10 байт. Посчитать количество байт,в которых сброшены 6 и 4 биты. Массив байтов размещается начиная с DS:0000h. Ответ должен...

Дан массив из 6 байт. Посчитать количество байт число единиц, в которых не превышает 3.
Дан массив из 6 байт. Посчитать количество байт число единиц, в ко- торых не превышает 3. блин люди немогу сделать.

Дан массив из 10 байт. Посчитать количество байт содержащих ровно три единицы
Здравствуйте, возникла проблема с решением задачи: &quot;Дан массив из 10 байт. Посчитать количество байт содержащих ровно три...


Искать еще темы с ответами

Или воспользуйтесь поиском по форуму:
16
Ответ Создать тему
Новые блоги и статьи
Как я обхитрил таблицу Word
Alexander-7 21.03.2026
Когда мигает курсор у внешнего края таблицы, и нам надо перейти на новую строку, а при нажатии Enter создается новый ряд таблицы с ячейками, то мы вместо нервных нажатий Энтеров мы пишем любые буквы. . .
Krabik - рыболовный бот для WoW 3.3.5a
AmbA 21.03.2026
без регистрации и смс. Это не торговля, приложение не содержит рекламы. Выполняет свою непосредственную задачу - автоматизацию рыбалки в WoW - и ничего более. Однако если админы будут против -. . .
Программный отбор значений справочника
Maks 21.03.2026
Установка программного отбора значений справочника "Сотрудники" из модуля формы документа. В качестве фильтра для отбора служит предопределенное значение перечислений. Процедура. . .
Переходник USB-CAN-GPIO
Eddy_Em 20.03.2026
Достаточно давно на работе возникла необходимость в переходнике CAN-USB с гальваноразвязкой, оный и был разработан. Однако, все меня терзала совесть, что аж 48-ногий МК используется так тупо: просто. . .
Оттенки серого
Argus19 18.03.2026
Оттенки серого Нашёл в интернете 3 прекрасных модуля: Модуль класса открытия диалога открытия/ сохранения файла на Win32 API; Модуль класса быстрого перекодирования цветного изображения в оттенки. . .
SDL3 для Desktop (MinGW): Рисуем цветные прямоугольники с помощью рисовальщика SDL3 на Си и C++
8Observer8 17.03.2026
Содержание блога Финальные проекты на Си и на C++: finish-rectangles-sdl3-c. zip finish-rectangles-sdl3-cpp. zip
Символические и жёсткие ссылки в Linux.
algri14 15.03.2026
Существует два типа ссылок — символические и жёсткие. Ссылка в Linux — это запись в каталоге, которая может указывать либо на inode «файла-ИСТОЧНИКА», тогда это будет «жёсткая ссылка» (hard link),. . .
[Owen Logic] Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора
ФедосеевПавел 14.03.2026
Поддержание уровня воды в резервуаре количеством включённых насосов: моделирование и выбор регулятора ВВЕДЕНИЕ Выполняя задание на управление насосной группой заполнения резервуара,. . .
КиберФорум - форум программистов, компьютерный форум, программирование
Powered by vBulletin
Copyright ©2000 - 2026, CyberForum.ru