Гость
Целевая тема:
Создать новую тему:
Автор:
Форумы / C++ [игнор отключен] [закрыт для гостей] / Отображение байтового формата в структуру. / 18 сообщений из 18, страница 1 из 1
12.08.2013, 11:30
    #38362864
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
Доброго времени!

Вопрос касается сетевого программирования. Есть предзаданный байтовый формат пакета. Их очень много различных. Хотелось бы минимальными усилиями и максимально эффективно описать формирователь каждого из пакетов.

Видел очень много закрытого продакшн кода использующего для этого PACKED структуры. Их не рассматриваем:

1-е это не переносимо, так как многие процессоры генерируют исключение при доступе к невыровненному адресу.
2-е даже когда это перенеслось, все работает медленно.
3-е сам умею их готовить :))

У меня по сабжу два вопроса

1. Какие методы применяете вы для работы с конкретными байтовыми форматами
2. Если знаете будет классно привести ссылку на оперсорсный код по сабжу

Код никсовых сетевых утилит смотрел, все пакеты разработаны так, чтобы доступ к полям автоматически получался выровненным, поэтому там просто описываются структуры без PACKED. Мне приходится иметь дело с пакетами с невыровненными полями. Самое первое что приходит в голову, так это делать как приведено ниже. Получается довольно громоздко. Причем, с ростом кол-ва полей, растет код функции установки каждого следующего поля. Посоветуйте какова общепринятая практика.

Код: plaintext
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.
47.
48.
49.
50.
#define raw_cast(arg) static_cast<uint8_t *>(static_cast<void *>(arg))
#define const_raw_cast(arg) static_cast<const uint8_t *>(static_cast<const void *>(arg))

union packet {

	enum type { REQ = 0, RESP = 1 };

	struct field {
		uint8_t type;
		uint8_t resend;
		uint32_t counter;
	} get;

	uint8_t raw[6];

	void set_type(uint8_t val) {
		std::copy(const_raw_cast(&val), const_raw_cast(&val) + sizeof(val), raw);
	}

	void set_resend(uint8_t val) {
		std::copy(const_raw_cast(&val), const_raw_cast(&val) + sizeof(val), raw + sizeof(field::type));
	}

	void set_counter(uint32_t val) {
		std::copy(const_raw_cast(&val), const_raw_cast(&val) + sizeof(val), raw + sizeof(field::type) + sizeof(field::resend));
	}

	void read_from(const uint8_t * src) {
		std::copy(src, src + sizeof(field::type), raw_cast(&get.type));
		std::copy(src + sizeof(field::type), src + sizeof(field::type) + sizeof(field::resend), raw_cast(&get.resend));
		std::copy(src + sizeof(field::type) + sizeof(field::resend), src + sizeof(field::type) + sizeof(field::resend) + sizeof(field::counter), raw_cast(&get.counter));
	}
};

..
// Пример записи пакета
packet pack;
pack.set_type(static_cast<uint8_t>(packet::REQ));
pack.set_resend(2);

send(pack.raw, sizeof(pack.raw));
...

...
// Пример считывания пакета
packet pack;
pack.read_from(buf);
use(pack.get.type);
use(pack.get.resend);
...
...
Рейтинг: 0 / 0
12.08.2013, 12:50
    #38362988
Dimitry Sibiryakov
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_Мне приходится иметь дело с пакетами с невыровненными полями.
Ну так тогда у тебя и выбора нет.

Лично я пишу что-то такое:
Код: sql
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.
struct TPacket{
   unsigned char p_type;
   virtual abstract void Read();
   virtual abstract void Write();
};

struct XPacket: public TPacket{
   int f1;
   char f2[10];
   Read();

};

void XPacket::Read()
{
   f1 = GetInt();
   GetString(f2, sizeof(f2));
}

void XPacket::Write()
{
   PutInt(f1);
   PutString(f2);
}

TPacket* PacketFactory(unsigned char packet_type)
{
   TPacket* Result = NULL;
   switch(packet_type)
   {
   case P_TYPE_X: Result = new XPacket; break;
   default throw "Unknown packet type";
   }
   Result->p_type = packet_type;
   return Result;
}

// чтение пакета
unsigned char packet_type=GetChar();
TPacket* packet = PacketFactory(packet_type);
packet->Read();

// запись пакета
PutChar(packet->p_type);
packet->Write();


Всю магию по преобразованию endianess и прочие внутриформатные мелочи берут на себя
геттеры. Всё знание о данных пакета инкапсулировано в классе. Вся иерархия классов легко
разбивается по модулям для упрощения чтения.
Posted via ActualForum NNTP Server 1.5
...
Рейтинг: 0 / 0
12.08.2013, 19:53
    #38363779
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
Мне в свои времена пришлось потратить не один человекомесяц на парсеры и мейкеры сетевых потоков.
Паковка структуры как и сами структуры не очень подходят если протокол обмена имеет диалекты
и может меняться и дополняться за время жизненного цикла.

Я использовал интерфейсный класс над бинарным дампом,
и синглетон-конфигуратор длин и смещений полей внутри дампа.

Сначала такой подход кажется сложным,
но потом решение получается более менее универсально-масштабируемо за разумное время.

зы А почему не XML ?( риторический вопрос)
...
Рейтинг: 0 / 0
13.08.2013, 10:33
    #38364243
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
Dimitry Sibiryakov

Спасибо, за интересный метод. Возможно применю его при создании иерархии пакетов. Но для заголовков оверхед виртуальных функций и наследования мне немного не подходит.

ДохтаР,
Не XML и не protobuf, и не boost::serialization и не много чего еще потому, что форматы пакетов предзаданы. Так-то я бы что-нибудь из перечисленного использовал для транспорта.


Вообще я поигрался с макросами и для заголовков пакетов у меня таки получился инструмент очень компактный, зацените (в перспективе его можно расширить и для поддержки массивов, но это еще вопрос надо ли):

Код: plaintext
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.
47.
48.
49.
50.
51.
52.
53.
54.
55.
56.
57.
58.
59.
#define raw_cast(arg) static_cast<uint8_t *>(static_cast<void *>(arg))
#define const_raw_cast(arg) static_cast<const uint8_t *>(static_cast<const void *>(arg))

#define _NUM_ARGS(X15, X14, X13, X12, X11, X10, X9, X8, X7, X6, X5, X4, X3, X2, X1, N, ...)   N
#define NUM_ARGS(...) _NUM_ARGS(__VA_ARGS__, 15, 14, 13, 12, 11, 10, 9, 8, 7, 6, 5, 4, 3, 2, 1)
#define EXPAND(X) X
#define FIRSTARG(X, ...) (X)
#define RESTARGS(X, ...) (__VA_ARGS__)
#define FOREACH(MACRO, LIST) FOREACH_(NUM_ARGS LIST, MACRO, LIST)
#define FOREACH_(N, M, LIST) FOREACH__(N, M, LIST)
#define FOREACH__(N, M, LIST) FOREACH_##N(M, LIST)
#define FOREACH_1(M, LIST) M LIST
#define FOREACH_2(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_1(M, RESTARGS LIST)
#define FOREACH_3(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_2(M, RESTARGS LIST)
#define FOREACH_4(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_3(M, RESTARGS LIST)
#define FOREACH_5(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_4(M, RESTARGS LIST)
#define FOREACH_6(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_5(M, RESTARGS LIST)
#define FOREACH_7(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_6(M, RESTARGS LIST)
#define FOREACH_8(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_7(M, RESTARGS LIST)
#define FOREACH_9(M, LIST) EXPAND(M FIRSTARG LIST) FOREACH_8(M, RESTARGS LIST)

#define npack_declare_field_internal(type, name) type name;
#define npack_declare_setter_internal(type, name) void set_##name(type name) { std::copy(const_raw_cast(&name), const_raw_cast(&name) + sizeof(type), raw + offsetof(packed, name)); }
#define npack_declare_getter_internal(type, name) std::copy(buf + offsetof(packed, name), buf + offsetof(packed, name) + sizeof(type), raw_cast(&get.name));
#define npack_declare_dumper_internal(type, name) do { out << "\t" << #name << ": " << static_cast<std::uint64_t>(get.name) << std::endl; } while (0);

#define npack_declare_field(list) npack_declare_field_internal list
#define npack_declare_setter(list) npack_declare_setter_internal list
#define npack_declare_getter(list) npack_declare_getter_internal list
#define npack_declare_dumper(list) npack_declare_dumper_internal list

#define declare_field(type, name) (type, name)
#define declare_packet(name, ...)\
	struct __attribute__((packed)) name##_packed { FOREACH(npack_declare_field, (__VA_ARGS__)) };\
	union name {\
		typedef name##_packed packed;\
		struct field { FOREACH(npack_declare_field, (__VA_ARGS__)) } get;\
		uint8_t raw[sizeof(packed)];\
		FOREACH(npack_declare_setter, (__VA_ARGS__));\
		void read(const uint8_t * buf) { FOREACH(npack_declare_getter, (__VA_ARGS__)) }\
		void zero() { std::fill(raw, raw + sizeof(raw), 0); }\
		template <class ostream>\
		void dump_raw(ostream & out) const {\
			out << #name << " raw: " << std::hex;\
			std::copy(raw, raw + sizeof(raw), std::ostream_iterator<std::uint64_t>(out, " "));\
			out << std::endl;\
		}\
		template <class ostream>\
		void dump(ostream & out) const {\
			out << std::hex;\
			out << #name << std::endl;\
			FOREACH(npack_declare_dumper, (__VA_ARGS__))\
		}\
	};\
	template <class ostream> ostream & operator<<(ostream & out, const name & p) {\
		p.dump_raw(out);\
		p.dump(out);\
		return out;\
	}



что в итоге позволяет описывать структуру пакета с аксессорами (как в первом посте) вот так:

Код: plaintext
1.
2.
3.
4.
5.
6.
declare_packet(
	packet
	, declare_field(uint8_t, type)
	, declare_field(uint8_t, resend)
	, declare_field(uint32_t, counter)
);



и работать с ней:

Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
12.
	packet pack;
	pack.zero();
	pack.set_type(1);
	pack.set_counter(2);

	std::cout << pack;

	packet pack2;
	pack2.zero();
	pack2.read(pack.raw);

	std::cout << pack2 << std::endl;



PS Код макрофорича нарыл вот здесь . Только все это С++11, так как основано на variadic макросах.
...
Рейтинг: 0 / 0
13.08.2013, 11:05
    #38364331
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_
ДохтаР,
Не XML и не protobuf, и не boost::serialization и не много чего еще потому, что форматы пакетов предзаданы . Так-то я бы что-нибудь из перечисленного использовал для транспорта.



У меня тоже были предзаданы
Спецификация формата обмена у вас есть ?
или вы реверсинженерите?
...
Рейтинг: 0 / 0
13.08.2013, 11:24
    #38364361
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаРsherzod_ДохтаР,
Не XML и не protobuf, и не boost::serialization и не много чего еще потому, что форматы пакетов предзаданы . Так-то я бы что-нибудь из перечисленного использовал для транспорта.
У меня тоже были предзаданы
Спецификация формата обмена у вас есть ?
или вы реверсинженерите? Описания пакетов есть. Тогда я не очень понял зачем здесь XML. Мне нужно формировать пакет в массиве байт и считывать пакет из массива байт, в обоих случаях, конечно же логические оффсеты и размеры в массиве и составляют этот самый формат. Обычные сетевые заморочки.

То есть заданы не структура сообщения, а именно структуры пакетов. То есть принимающая сторона или отправляющая, ожидают конкретные оффсеты и размеры в байтовом представлении.
...
Рейтинг: 0 / 0
13.08.2013, 11:31
    #38364371
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_ДохтаРпропущено...
У меня тоже были предзаданы
Спецификация формата обмена у вас есть ?
или вы реверсинженерите? Описания пакетов есть. Тогда я не очень понял зачем здесь XML.

По поводу XML был риторический вопрос.

sherzod_Мне нужно формировать пакет в массиве байт и считывать пакет из массива байт, в обоих случаях, конечно же логические оффсеты и размеры в массиве и составляют этот самый формат. Обычные сетевые заморочки.

То есть заданы не структура сообщения, а именно структуры пакетов. То есть принимающая сторона или отправляющая, ожидают конкретные оффсеты и размеры в байтовом представлении.

В общем случае да, если нет метаполей, которые определяют длину поля.

у меня такие извраты былиLL может быть длиной 1 или 2 байта. Например, если оно упаковано как один шестнадцатеричный байт, 0x27 означает, что далее следует 27 байт поля VAR. Если формат ASCII, два байта 0x32, 0x37 означают, что далее следует 27 байт (так как 0x32 - это код символа '2' в кодировке ASCII и т.п.). 3-цифирная длина поля LLL использует 2 байта с лидирующим 0x00 в упакованном режиме, или 3 байта в формате ASCII. Формат элемента данных VAR зависит от типа элемента данных. Если число было упаковано, то 87456 будет представлено 3 байтами 0x087456. В формате ASCII будет использоваться один байт для каждой цифры или символа, оно будет выглядеть, как 0x38


Длина разных полей могла кодироваться по разному , для каждого поля нужно было задавать свое правило.
...
Рейтинг: 0 / 0
13.08.2013, 12:10
    #38364449
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаРВ общем случае да, если нет метаполей, которые определяют длину поля.

у меня такие извраты былиLL может быть длиной 1 или 2 байта. Например, если оно упаковано как один шестнадцатеричный байт, 0x27 означает, что далее следует 27 байт поля VAR. Если формат ASCII, два байта 0x32, 0x37 означают, что далее следует 27 байт (так как 0x32 - это код символа '2' в кодировке ASCII и т.п.). 3-цифирная длина поля LLL использует 2 байта с лидирующим 0x00 в упакованном режиме, или 3 байта в формате ASCII. Формат элемента данных VAR зависит от типа элемента данных. Если число было упаковано, то 87456 будет представлено 3 байтами 0x087456. В формате ASCII будет использоваться один байт для каждой цифры или символа, оно будет выглядеть, как 0x38

Длина разных полей могла кодироваться по разному , для каждого поля нужно было задавать свое правило. А, ну да, есть еще же всякие условности делающие структуру неоднозначной. Мне придется с этим еще только столкнуться. Для статичных заголовков, то что я привел выше на мой взгляд идеально. А для динамичных это да ручками придется делать.
...
Рейтинг: 0 / 0
13.08.2013, 12:18
    #38364462
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_ДохтаРВ общем случае да, если нет метаполей, которые определяют длину поля.

пропущено...

Длина разных полей могла кодироваться по разному , для каждого поля нужно было задавать свое правило. А, ну да, есть еще же всякие условности делающие структуру неоднозначной. Мне придется с этим еще только столкнуться. Для статичных заголовков, то что я привел выше на мой взгляд идеально. А для динамичных это да ручками придется делать.


Ручками поковыряться в коде, а потом подебаджить по локоть в ......
и ручками в конфигурации , как говорят в Одессе 2 большие разницы.

Если писать универсальный код под конфигуратор , особое внимание нужно уделить отлову
внештатных ситуаций , что бы при подсовывании конфига , по которуму нельзя распарсить правильный
пакет вся программа не падала в кору.

У меня библиотека парсинга-сборки около 7 000 строк получилась.
где то 30 -40% кода в генераторах и обработчиках исключений .
...
Рейтинг: 0 / 0
13.08.2013, 12:27
    #38364474
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаР

если можно, разъясните подробнее в чем заключается ваш метод. Структуру метода, ключевые сущности, из вашего короткого описания, видимо по неопытности, я не могу ее представить:

ДохтаРЯ использовал интерфейсный класс над бинарным дампом,
и синглетон-конфигуратор длин и смещений полей внутри дампа.
...
Рейтинг: 0 / 0
13.08.2013, 13:02
    #38364530
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_ДохтаР

если можно, разъясните подробнее в чем заключается ваш метод. Структуру метода, ключевые сущности, из вашего короткого описания, видимо по неопытности, я не могу ее представить:

ДохтаРЯ использовал интерфейсный класс над бинарным дампом,
и синглетон-конфигуратор длин и смещений полей внутри дампа.


Класс поле.
Класс сообщение ( массив или список полей).
Иерархия классов конфигуратор поля( опрделяет длины смещения, нетривиальные кодировки
и прочие параметры из конфигурационого файла).
Класс конфигуратор сообщения ( массив, список или дерево конфигураторов полей).
Иерархия классов нетривиальных кодировок, для правильного преобразования LL и LLL в числа из цитаты выше итд итп.

Класс конфигуратор загружет метаинформацию из конфигурационного файла .


Класс сообщение при создании обьекта инстанциируется конфиругатором.


3 класса в иерархии наследованной от std:logic_error один для выброса исключений
с уровня обработчика поля, другой для выброса с уровня отработчика сообщения.
Третий класс в иерархии обработки ошибок - отладочный.
Список ошибок парсинга- сборки с расшифровками кодов .


Циклы и рекурсии в обработчике сообщений.

Приблизительно так.
...
Рейтинг: 0 / 0
13.08.2013, 13:17
    #38364559
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаР,

Да вобщем-то, интересно. Своеобразное обскриптовывание парсинга. Можете показать пример конфигурации поля если это возможно?
...
Рейтинг: 0 / 0
13.08.2013, 13:46
    #38364624
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_ДохтаР,

Да вобщем-то, интересно. Своеобразное обскриптовывание парсинга. Можете показать пример конфигурации поля если это возможно?


Это было 10 лет назад , я даже не помню где исходники.
приблизительно так
Код: plaintext
1.
2.
3.
4.
5.
6.
7.
8.
9.
10.
11.
/*
1	b 8
2	n ..19	Primary account number (PAN)
3	n 6	Processing code
4	n 12	Amount, transaction
*/

1  F,8,L,BIN,0x00  BITMAP
2  V,2,R,BCD,0x00;19,L,ASCI,0xFF  PAN
3  F,6,L,BCD,0x00  PROCCODE
4  F,12,R BCD,0x00 AMOUNT 




V - переменная длина поля( информаци о длине поля содержится в метаинформации поля)

описание параметров метаинформации длины поля:
2 - длина длины ( LL из цитаты).
R- правило выравнивание по левому или право краю.
BCD - имя нетривиальной кодировки длины.
0x00 - символ заполения пустоты .

F - фиксированная длина

19 - максимальная длина поля для полей переменной длины или длина для фиксированной.
L - правило выравнивание поля по левому или право краю.
ASCI кодировка поля
0xFF- символ заполения пустоты

AMOUNT - название поля.
...
Рейтинг: 0 / 0
13.08.2013, 14:00
    #38364644
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаР,

понятно спасибо. По сути это довольно простой конечный автомат.
...
Рейтинг: 0 / 0
13.08.2013, 14:08
    #38364660
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
sherzod_ДохтаР,

понятно спасибо. По сути это довольно простой конечный автомат.

Был рад помочь.

Логика конечного автомата простая,
но на реализацию ушло не меньше 3 месяцев.
...
Рейтинг: 0 / 0
13.08.2013, 14:23
    #38364677
sherzod_
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
ДохтаР,

да я конечно имел в виду механизм работы:). Безусловно написание грамматики для него дело непростое совсем.
...
Рейтинг: 0 / 0
13.08.2013, 14:43
    #38364715
Изопропил
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
в протоколе H.323 для описания структур используется ASN.1
...
Рейтинг: 0 / 0
13.08.2013, 14:43
    #38364716
ДохтаР
Участник
Скрыть профиль Поместить в игнор-лист Сообщения автора в теме
Отображение байтового формата в структуру.
Все это затевалось для того что бы эту штуку
Код: plaintext
1.
3  F,6,L,BCD,0x00  PROCCODE



На лету для прикладного разработчика можно было преобразовать в эту
Код: plaintext
1.
2.
3.
3.1  F,2,L,BCD,0x00  PROCCODE
3.2  F,2,L,BCD,0x00  PROC_ACC_FROM
3.3  F,2,L,BCD,0x00  PROC_ACC_TO



Не изменив ни строчки кода в парсере-сборщике.
...
Рейтинг: 0 / 0
Форумы / C++ [игнор отключен] [закрыт для гостей] / Отображение байтового формата в структуру. / 18 сообщений из 18, страница 1 из 1
Найденые пользователи ...
Разблокировать пользователей ...
Читали форум (0):
Пользователи онлайн (0):
x
x
Закрыть


Просмотр
0 / 0
Close
Debug Console [Select Text]