Отправка в сеть структур данных с полями переменной длины
От: Аноним  
Дата: 06.05.09 05:08
Оценка:
Доброго времени суток.

Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:

struct Struct1 {
    uint8 Tag; // = 10
    uint16 Length;
    uint8* Data;
};

struct Command1 {
    uint8 Tag; // = 1
    uint16 Length;
    Struct1 Data1;
};


Как видите, у некоторых структур имеются поля переменной длины.
Допустим мы инициализируем эти структуры примерно следующим образом:

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: Отправка в сеть структур данных с полями переменной длин
От: Аноним  
Дата: 06.05.09 05:20
Оценка: 1 (1) +1
Здравствуйте, Аноним, Вы писали:

А>Доброго времени суток.


А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:


Сериализация тебе в помощь.
Re[2]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 05:28
Оценка:
Кстати, забыл сказать, что реализация протокола осуществляется под Linux
Re: Отправка в сеть структур данных с полями переменной длин
От: Odi$$ey Россия http://malgarr.blogspot.com/
Дата: 06.05.09 05:42
Оценка: 9 (1) +1
Здравствуйте, <Аноним>, Вы писали:

А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети.


Google Protocol Buffers?
... << RSDN@Home 1.2.0 alpha 4 rev. 1207>>
Re: Отправка в сеть структур данных с полями переменной длин
От: Чили Россия  
Дата: 06.05.09 06:53
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Доброго времени суток.


А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:


А>
А>struct Struct1 {
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8* Data;
А>};

А>struct Command1 {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    Struct1 Data1;
А>};
А>


А>Как видите, у некоторых структур имеются поля переменной длины.

А>Допустим мы инициализируем эти структуры примерно следующим образом:

А>
//А>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 *cmd1 = (Command1*)malloc(2*(sizeof(uint8) + sizeof(uint16)) + sizeof(uint8) * 50);
cmd1->Data1.Tag = 10;
cmd1->Data1.Length = sizeof(uint8) + sizeof(uint16) + sizeof(uint8) * 50;
strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
cmd1->Tag = 1;
cmd1->Length = sizeof(uint8) + sizeof(uint16) + cmd1->Data1.Length;

А>


А>Далее необходимо отправить структуру 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]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 07:46
Оценка:
struct Struct1 {
    uint8 Tag; // = 10
    uint16 Length;
    uint8* Data;
};

struct Command1 {
    uint8 Tag; // = 1
    uint16 Length;
    Struct1 Data1;
};

Command1 *cmd1 = (Command1*)malloc(2*(sizeof(uint8) + sizeof(uint16)) + sizeof(uint8) * 50);
cmd1->>Data1.Tag = 10;
cmd1->>Data1.Length = sizeof(uint8) + sizeof(uint16) + sizeof(uint8) * 50;
strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
cmd1->>Tag = 1;
cmd1->>Length = sizeof(uint8) + sizeof(uint16) + cmd1->Data1.Length;

int res = send(sock, cmd1, cmd1->Length, 0);


А вы пробовали это запускать? У меня например падает на строке
strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
Re[3]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 07:57
Оценка:
Здравствуйте, Аноним, Вы писали:

А>
А>struct Struct1 {
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8* Data;
А>};

А>struct Command1 {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    Struct1 Data1;
А>};

А>

А>
А>Command1 *cmd1 = (Command1*)malloc(2*(sizeof(uint8) + sizeof(uint16)) + sizeof(uint8) * 50);
cmd1->>>Data1.Tag = 10;
cmd1->>>Data1.Length = sizeof(uint8) + sizeof(uint16) + sizeof(uint8) * 50;
А>strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);
cmd1->>>Tag = 1;
cmd1->>>Length = sizeof(uint8) + sizeof(uint16) + cmd1->Data1.Length;
А>

А>
А>int res = send(sock, cmd1, cmd1->Length, 0);
А>


А>А вы пробовали это запускать? У меня например падает на строке

А>strncpy((char*)cmd1->Data1.Data, "bla bla bla bla bla!", 20);

В дебаге видно, что после выделения участка памяти указатель cmd1->Data1.Data указывает на 0x000000. А потом происходит попытка копирования по нулевому адресу со всеми вытекающими из этого последствиями...
Re: Отправка в сеть структур данных с полями переменной длин
От: Кывт Ниоткуда  
Дата: 06.05.09 08:07
Оценка: -2 :)
Очевидно, стандартные механизмы для описания структур данных в C++ (struct и class) не рассчитаны на работу с полями переменной длины, так что в эту сторону даже смотреть не стоит.

Здесь нужен некий специальный механизм, как выше правильно заметили, наподобие сериализации или Google Protocol Buffers.
Re: Отправка в сеть структур данных с полями переменной длин
От: Alexey Frolov Беларусь  
Дата: 06.05.09 08:16
Оценка: 1 (1) +2
Здравствуйте, Аноним, Вы писали:

А>Доброго времени суток.


А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:


А>
А>struct Struct1 {
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8* Data;
А>};

А>struct Command1 {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    Struct1 Data1;
А>};
А>



Есть классический подход, используется как минимум 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]: Отправка в сеть структур данных с полями переменной д
От: Alexey Frolov Беларусь  
Дата: 06.05.09 08:20
Оценка:
сори, небольшая неточность

uint8 *cmdbuf = new uint8[sizeof(Command1) + sizeof(uint8) * 50];
Command1 *cmd1 = (Command1 *) cmdbuf;

cmd1->Hdr.Tag = 1;
cmd1->Hdr.Length = sizeof(Command1) + sizeof(uint8) * 50;

...

int res = send(sock, cmd1, cmd1->Hdr.Length, 0);
Re[2]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 08:49
Оценка:
Здравствуйте, Alexey Frolov, Вы писали:

AF>Здравствуйте, Аноним, Вы писали:


А>>Доброго времени суток.


А>>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:


А>>
А>>struct Struct1 {
А>>    uint8 Tag; // = 10
А>>    uint16 Length;
А>>    uint8* Data;
А>>};

А>>struct Command1 {
А>>    uint8 Tag; // = 1
А>>    uint16 Length;
А>>    Struct1 Data1;
А>>};
А>>



AF>Есть классический подход, используется как минимум microsoft-ом и очень часто пока еще


AF>
AF>struct CommandHeader {
AF>    uint8 Tag; 
AF>    uint16 Length;

AF>        // хоть это и не важно, я бы предпочел
AF>        // uint16 Length;
AF>        // uint8 Tag;    
AF>};


AF>struct Command1 {
AF>    CommandHeader Hdr; // = {1, Command1Size)}
AF>    uint8 Data[1];
AF>};

AF>struct Command2 {
AF>    CommandHeader Hdr; // = {10, sizeof(Command2)}
AF>    uint16 Param1;
AF>    uint16 Param2;
AF>    uint16 Param3;
AF>    uint16 Param4;
AF>};
AF>


AF>используем примерно так


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: Отправка в сеть структур данных с полями переменной длин
От: Libra Россия  
Дата: 06.05.09 08:58
Оценка:
Здравствуйте, Аноним, Вы писали:

можно еще посмотреть в сторону XDR

# man xdr
Species come and go, but the earth stands forever fast...
Re[3]: Отправка в сеть структур данных с полями переменной д
От: Чили Россия  
Дата: 06.05.09 09:01
Оценка:
Здравствуйте, Аноним, Вы писали:

А>[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: Отправка в сеть структур данных с полями переменной длин
От: Аноним  
Дата: 06.05.09 09:03
Оценка:
делал нечто похожее, язык C, компилятор gcc.


typedef struct
{
    uint8 Tag; // = 10
    uint16 Length;
    uint8 payload[]; 
} tStruct1 ;

typedef struct  {
    uint8 Tag; // = 1
    uint16 Length;
    uint8 payload[]; 
} tCommand1;



uint8 tmp[1024];
tCommand1 *pCmd = (tCommand1 *)tmp;
pCmd->Tag = 10;

tStruct1* pData = (tStruct1*)pCmd->payload;

strncpy((char*)pData->payload, "bla bla bla bla bla!", 20);
pData->Length = strlen("bla bla bla bla bla!");
pCmd->Length = pData->Length + sizeof(*pData) + sizeof(*pCmd);

send(sock, cmd1, pCmd->Length, 0);
Re: Отправка в сеть структур данных с полями переменной длин
От: R.O. Prokopiev Россия http://127.0.0.1/
Дата: 06.05.09 09:08
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:


А>
А>struct Struct1 {
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8* Data;
А>};

А>struct Command1 {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    Struct1 Data1;
А>};
А>


Кроме всего прочего, здесь видно еще 2 недочета:
1) Выравнивание. Между полями компилятор может напихать заполнители. На худой конец можно сделать так
#pragma pack(push, 1)
struct {...};
#pragma pack(pop)


Хотя, честно говоря, не знаю, насколько это по стандарту.

Не все архитектуры поддерживают доступ по невыровненным адресам.

Я бы сделал по-другому — поэлементно скопировал (memcpy) в байтовый буфер, а оттуда бы отправлял.

2) Порядок байт в слове может отличаться от того, который задан в протоколе.
Если к примеру Length по спецификации передается в сетевом порядке байт,
то перед отправкой нужно применять htons, после принятия ntohs.
Если по спецификации передача идет в little-endian, видимо придется использовать
самодельные трюки.
Re[2]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 09:09
Оценка:
Здравствуйте, Аноним, Вы писали:

А>делал нечто похожее, язык C, компилятор gcc.



А>
А>typedef struct
А>{
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8 payload[]; 
А>} tStruct1 ;

А>typedef struct  {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    uint8 payload[]; 
А>} tCommand1;



А>uint8 tmp[1024];
А>tCommand1 *pCmd = (tCommand1 *)tmp;
pCmd->>Tag = 10;

А>tStruct1* pData = (tStruct1*)pCmd->payload;

А>strncpy((char*)pData->payload, "bla bla bla bla bla!", 20);
А>pData->Length = strlen("bla bla bla bla bla!");
pCmd->>Length = pData->Length + sizeof(*pData) + sizeof(*pCmd);

А>send(sock, cmd1, pCmd->Length, 0);
А>
Re[3]: Отправка в сеть структур данных с полями переменной д
От: Аноним  
Дата: 06.05.09 09:16
Оценка:
+ не стоит забывать про выравнивание и упаковку структур.
Re[3]: Отправка в сеть структур данных с полями переменной д
От: Alexey Frolov Беларусь  
Дата: 06.05.09 11:07
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Всё бы ничего, да вот только такой подход рушиться в случае, например, такой ситуации (а такие ситуации в спецификации на структуру протокольных сообщений встречаются часто):



А>
А>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
А>};
А>


Никто не обещал что это будет очень удобный в использовании механизм, естесственно реализация должна быть корректной и никаких перекрываний быть не должно. Если хотите что-то изящное, удобное в использовании и надежно защищенное от ошибок по невнимательности, то используйте что вам советовали выше: сериализация, и т.д. Тем не менее такой подход используется, здесь пример решения вашей проблемы microsoft-ом. Не удобно, но работает. И правильно тут заметили, проблемы с выравниванием тоже надо решать
Re: Отправка в сеть структур данных с полями переменной длин
От: vnp  
Дата: 06.05.09 16:47
Оценка:
Здравствуйте, Аноним, Вы писали:

А>Насколько я понимаю, такая отправка не является корректной, так как мы отправляем последовательность байтов, начинающихся по адресу &cmd1 и имеющую длину cmd1.Length, но cmd1.s1.Data — это указатель, который указывает на динамическую память, которая в эту последовательность, естественно не попадает.


man sendmsg (особое внимание уделить msghdr.iovec). Но, как указали выше, проблемы с выравниванием и порядком байт все равно будут.
Re: Отправка в сеть структур данных с полями переменной длин
От: Dmitry V. Krivenok Россия  
Дата: 06.05.09 18:18
Оценка: 1 (1)
Здравствуйте, Аноним, Вы писали:

А>Доброго времени суток.


А>Есть задача реализовать некоторый протокол, в котором имеются определённые структуры данных. Эти структуры необходимо передавать по сети. Приведу пример структур:



Посмотри в сторону ASN.1 и любого способа кодирования (например BER — basic encoding rules).
Компилятор ASN.1 -> C можно взять с http://lionet.info/asn1c
Сам недавно его использовал для генерации SNMP-пакетов (в SNMP иначе нельзя так как протокол
описан в терминах ASN.1).
Реализация сильно упрощается, хотя на вопросы производительности нужно обратить особое
внимание (если это важно в твоей задаче).


А>
А>struct Struct1 {
А>    uint8 Tag; // = 10
А>    uint16 Length;
А>    uint8* Data;
А>};

А>struct Command1 {
А>    uint8 Tag; // = 1
А>    uint16 Length;
А>    Struct1 Data1;
А>};
А>


А>Как видите, у некоторых структур имеются поля переменной длины.

А>Допустим мы инициализируем эти структуры примерно следующим образом:

А>
А>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.


А>Ситуация со структурами, содержащими поля переменной длины вполне типичная при реализации различных протоколов... Хотелось бы услышать советы и рекомендации, как необходимо организовывать структуры и/или отправку этих структур, чтобы избежать описанной выше проблемы.


А>Заранее спасибо.
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.