Отправка в сеть структур данных с полями переменной длины
От:
Аноним
Дата:
06.05.09 05:08
Оценка:
Доброго времени суток.
Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
Как видите, у некоторых структур имеются поля переменной длины.
Допустим мы инициализируем эти структуры примерно следующим образом:
Struct1 s1 = {10, sizeof(uint8) + sizeof(uint16) + sizeof(uint8) * 50, new uint8[50] };
strncpy((char*)s1.Data, "bla bla bla bla bla!", 20);
Command1 cmd1 = {1, sizeof(uint8) + sizeof(uint16) + s1.Length, s1};
Далее необходимо отправить структуру Command1 в сеть:
int res = send(sock, &cmd1, cmd1.Length, 0);
Насколько я понимаю, такая отправка не является корректной, так как мы отправляем последовательность байтов, начинающихся по адресу &cmd1 и имеющую длину cmd1.Length, но cmd1.s1.Data — это указатель, который указывает на динамическую память, которая в эту последовательность, естественно не попадает.
То есть мы вместо того, чтобы отправить строку "bla bla bla bla bla!" либо отправим какой-нибудь мусор, либо вообще рухнем по segmentation fault.
Ситуация со структурами, содержащими поля переменной длины вполне типичная при реализации различных протоколов... Хотелось бы услышать советы и рекомендации, как необходимо организовывать структуры и/или отправку этих структур, чтобы избежать описанной выше проблемы.
Заранее спасибо.
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>Доброго времени суток.
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
Сериализация тебе в помощь.
Re[2]: Отправка в сеть структур данных с полями переменной д
От:
Аноним
Дата:
06.05.09 05:28
Оценка:
Кстати, забыл сказать, что реализация протокола осуществляется под Linux
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, <Аноним>, Вы писали:
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети.
Здравствуйте, Аноним, Вы писали:
А>Доброго времени суток.
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
А>
А>Далее необходимо отправить структуру Command1 в сеть:
А>
А>int res = send(sock, &cmd1, cmd1.Length, 0);
int res = send(sock, cmd1, cmd1->Length, 0);
А>
А>Насколько я понимаю, такая отправка не является корректной, так как мы отправляем последовательность байтов, начинающихся по адресу &cmd1 и имеющую длину cmd1.Length, но cmd1.s1.Data — это указатель, который указывает на динамическую память, которая в эту последовательность, естественно не попадает. А>То есть мы вместо того, чтобы отправить строку "bla bla bla bla bla!" либо отправим какой-нибудь мусор, либо вообще рухнем по segmentation fault.
А>Ситуация со структурами, содержащими поля переменной длины вполне типичная при реализации различных протоколов... Хотелось бы услышать советы и рекомендации, как необходимо организовывать структуры и/или отправку этих структур, чтобы избежать описанной выше проблемы.
А>Заранее спасибо.
Re[2]: Отправка в сеть структур данных с полями переменной д
А>А вы пробовали это запускать? У меня например падает на строке А>strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
В дебаге видно, что после выделения участка памяти указатель cmd1->Data1.Data указывает на 0x000000. А потом происходит попытка копирования по нулевому адресу со всеми вытекающими из этого последствиями...
Re: Отправка в сеть структур данных с полями переменной длин
Очевидно, стандартные механизмы для описания структур данных в C++ (struct и class) не рассчитаны на работу с полями переменной длины, так что в эту сторону даже смотреть не стоит.
Здесь нужен некий специальный механизм, как выше правильно заметили, наподобие сериализации или Google Protocol Buffers.
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>Доброго времени суток.
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
А>
Есть классический подход, используется как минимум microsoft-ом и очень часто пока еще
struct CommandHeader {
uint8 Tag;
uint16 Length;
// хоть это и не важно, я бы предпочел
// uint16 Length;
// uint8 Tag;
};
struct Command1 {
CommandHeader Hdr; // = {1, Command1Size)}
uint8 Data[1];
};
struct Command2 {
CommandHeader Hdr; // = {10, sizeof(Command2)}
uint16 Param1;
uint16 Param2;
uint16 Param3;
uint16 Param4;
};
используем примерно так
uint8 *cmdbuf = new uint8[sizeof(Command1) + sizeof(uint8) * 50];
Command1 *cmd1 = (Command1 *) cmdbuf;
cmd1->Tag = 1;
cmd1->Length = sizeof(Command1) + sizeof(uint8) * 50;
strncpy((char *) cmd1->Data, "bla bla bla bla bla!", 50); // если Data - это строка заканчивающаяся нулем,
// то еще нужно убедиться что последний символ действительно 0,
// например так, *(cmd1.Data + 50) = 0;
// (именно 50, так как еще один символ у нас есть uint8 Data[1])int res = send(sock, cmd1, cmd1->Length, 0);
Re[2]: Отправка в сеть структур данных с полями переменной д
Re[2]: Отправка в сеть структур данных с полями переменной д
От:
Аноним
Дата:
06.05.09 08:49
Оценка:
Здравствуйте, Alexey Frolov, Вы писали:
AF>Здравствуйте, Аноним, Вы писали:
А>>Доброго времени суток.
А>>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
А>>
AF>uint8 *cmdbuf = new uint8[sizeof(Command1) + sizeof(uint8) * 50];
AF>Command1 *cmd1 = (Command1 *) cmdbuf;
cmd1->>Tag = 1;
cmd1->>Length = sizeof(Command1) + sizeof(uint8) * 50;
AF>strncpy((char *) cmd1->Data, "bla bla bla bla bla!", 50); // если Data - это строка заканчивающаяся нулем,
AF> // то еще нужно убедиться что последний символ действительно 0,
AF> // например так, *(cmd1.Data + 50) = 0;
AF> // (именно 50, так как еще один символ у нас есть uint8 Data[1])
AF>int res = send(sock, cmd1, cmd1->Length, 0);
AF>
Всё бы ничего, да вот только такой подход рушиться в случае, например, такой ситуации (а такие ситуации в спецификации на структуру протокольных сообщений встречаются часто):
struct CommandHeader {
uint8 Tag;
uint16 Length;
};
struct Command1 {
CommandHeader Hdr; // = {1, Command1Size)}
uint8 Data[1];
};
struct Command2 {
CommandHeader Hdr; // = {2, Command2Size)}
Command1 ParamCmd1;
uint16 Param2; // = 222
};
uint8 *cmdbuf = new uint8[sizeof(Command2) + sizeof(uint8) * 50];
Command2 *cmd2 = (Command2 *) cmdbuf;
cmd2->Hdr.Tag = 2;
cmd2->Hdr.Length = sizeof(Command2) + sizeof(uint8) * 50;
strncpy((char *) cmd2->ParamCmd1.Data, "bla bla bla bla bla!", 50);
cmd2->Param2 = 222; // это записью затрётся память, содержашая вышескопированную строчку (от той строки останется только кусок размером sizeof(uint8))
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>[ccode] А>struct Struct1 { А> uint8 Tag; // = 10 А> uint16 Length; А> uint8* Data; А>};
А>struct Command1 { А> uint8 Tag; // = 1 А> uint16 Length; А> Struct1 Data1; А>};
А>А вы пробовали это запускать? У меня например падает на строке А>strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
Попробуйте вот так:
strncpy((((char*)cmd1)+2*(sizeof(uint8)+sizeof(uint16))), "bla bla bla bla bla!", 20);
т.е. Вы копируете строку по адресу поля Data.
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
А>
Хотя, честно говоря, не знаю, насколько это по стандарту.
Не все архитектуры поддерживают доступ по невыровненным адресам.
Я бы сделал по-другому — поэлементно скопировал (memcpy) в байтовый буфер, а оттуда бы отправлял.
2) Порядок байт в слове может отличаться от того, который задан в протоколе.
Если к примеру Length по спецификации передается в сетевом порядке байт,
то перед отправкой нужно применять htons, после принятия ntohs.
Если по спецификации передача идет в little-endian, видимо придется использовать
самодельные трюки.
Re[2]: Отправка в сеть структур данных с полями переменной д
От:
Аноним
Дата:
06.05.09 09:09
Оценка:
Здравствуйте, Аноним, Вы писали:
А>делал нечто похожее, язык C, компилятор gcc.
Здравствуйте, Аноним, Вы писали:
А>Всё бы ничего, да вот только такой подход рушиться в случае, например, такой ситуации (а такие ситуации в спецификации на структуру протокольных сообщений встречаются часто):
Никто не обещал что это будет очень удобный в использовании механизм, естесственно реализация должна быть корректной и никаких перекрываний быть не должно. Если хотите что-то изящное, удобное в использовании и надежно защищенное от ошибок по невнимательности, то используйте что вам советовали выше: сериализация, и т.д. Тем не менее такой подход используется, здесь пример решения вашей проблемы microsoft-ом. Не удобно, но работает. И правильно тут заметили, проблемы с выравниванием тоже надо решать
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>Насколько я понимаю, такая отправка не является корректной, так как мы отправляем последовательность байтов, начинающихся по адресу &cmd1 и имеющую длину cmd1.Length, но cmd1.s1.Data — это указатель, который указывает на динамическую память, которая в эту последовательность, естественно не попадает.
man sendmsg (особое внимание уделить msghdr.iovec). Но, как указали выше, проблемы с выравниванием и порядком байт все равно будут.
Re: Отправка в сеть структур данных с полями переменной длин
Здравствуйте, Аноним, Вы писали:
А>Доброго времени суток.
А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:
Посмотри в сторону ASN.1 и любого способа кодирования (например BER — basic encoding rules).
Компилятор ASN.1 -> C можно взять с http://lionet.info/asn1c
Сам недавно его использовал для генерации SNMP-пакетов (в SNMP иначе нельзя так как протокол
описан в терминах ASN.1).
Реализация сильно упрощается, хотя на вопросы производительности нужно обратить особое
внимание (если это важно в твоей задаче).
А>Как видите, у некоторых структур имеются поля переменной длины. А>Допустим мы инициализируем эти структуры примерно следующим образом:
А>
А>Struct1 s1 = {10, sizeof(uint8) + sizeof(uint16) + sizeof(uint8) * 50, new uint8[50] };
А>strncpy((char*)s1.Data, "bla bla bla bla bla!", 20);
А>Command1 cmd1 = {1, sizeof(uint8) + sizeof(uint16) + s1.Length, s1};
А>
А>Далее необходимо отправить структуру Command1 в сеть:
А>
А>int res = send(sock, &cmd1, cmd1.Length, 0);
А>
А>Насколько я понимаю, такая отправка не является корректной, так как мы отправляем последовательность байтов, начинающихся по адресу &cmd1 и имеющую длину cmd1.Length, но cmd1.s1.Data — это указатель, который указывает на динамическую память, которая в эту последовательность, естественно не попадает. А>То есть мы вместо того, чтобы отправить строку "bla bla bla bla bla!" либо отправим какой-нибудь мусор, либо вообще рухнем по segmentation fault.
А>Ситуация со структурами, содержащими поля переменной длины вполне типичная при реализации различных протоколов... Хотелось бы услышать советы и рекомендации, как необходимо организовывать структуры и/или отправку этих структур, чтобы избежать описанной выше проблемы.
А>Заранее спасибо.