Visual C ++ (.NET 4.0) - Сериализация / Копирование пользовательской структуры в Byte-Array для отправки через NetworkStream - PullRequest
1 голос
/ 18 августа 2011

Я работаю над TCP-коммуникацией с программой другого человека.Для правильной связи он определил структуру заголовка для сообщений, отправляемых через TCP.Это определяется следующим образом:

typedef struct
{
    uint16_t  number1;                 
    uint16_t  number2;
    char      name1[64];
    char      name2[64];
    uint32_t  size;
} headerStruct;

Структура каким-то образом заполнена, наиболее важно headerStruct.size устанавливает количество байтов после заголовка и размер сообщений, которые должны бытьотправлено.

Теперь я попытался сериализовать заголовок следующим образом:

headerStruct sourceHeader;
// ... filling Header-Struct here ...

array<Byte>^ result = gcnew array<Byte>(520);  // I tried sizeof(headerStruct) instead of '520', but then I get errors
double allreadyCopied = 0;

// serialize uint16_t 'number1'
array<Byte>^ array1 = BitConverter::GetBytes( sourceHeader.number1 );
Array::Copy(array1 , 0, result, allreadyCopied, array1 ->Length);
allreadyCopied += array1 ->Length;

// serialize uint16_t 'number2'
array<Byte>^ array2 = BitConverter::GetBytes( sourceHeader.number2 );
Array::Copy(array2 , 0, result, allreadyCopied, array2->Length);
allreadyCopied += array2->Length;

// serialize char-Array 'name1'    
for (int i = 0; i < sizeof(sourceHeader.name1); i++) 
{
    array<Byte>^ arrayName1 = BitConverter::GetBytes( sourceHeader.name1[i] );
    Array::Copy(arrayName1 , 0, result, allreadyCopied, arrayName1->Length);
    allreadyCopied += arrayName1->Length;
}

// serialize char-Array 'name2'       
for (int i = 0; i < sizeof(sourceHeader.name2); i++) 
{
    array<Byte>^ arrayName2 = BitConverter::GetBytes( sourceHeader.name2[i] );
    Array::Copy(arrayName2 , 0, result, allreadyCopied, arrayName2->Length);
    allreadyCopied += arrayName2->Length;
}

// serialize uint32_t 'size'        
array<Byte>^ arraySize = BitConverter::GetBytes( sourceHeader.size);
Array::Copy(arraySize, 0, result, allreadyCopied, arraySize->Length);
allreadyCopied += arraySize->Length;

Теперь мне кажется, что это работает, НО :

  1. Я попытался использовать sizeof (headerStruct) вместо жестко заданного значения 520 для размера в массиве результатов.Я просто «вычислил» его с помощью переменной allreadyCopied .Но почему они разные?Где моя ошибка здесь?

  2. Этот метод, безусловно, не очень элегантный.Есть лучший способ сделать это?Я попробовал маршал-класс, но мне не удалось заставить его работать!

  3. Почему-то кажется, что такой подход неправильный.Это правильный путь или я совершенно не прав?

Любая помощь очень ценится!Спасибо!

Редактировать:

OK только что проверил:

sizeof(headerStruct);       // = 136;
sizeof(char);               // = 1;
sizeof(sourceHeader.name1); // = 64;

НО

arrayName1->Length;         // = 4;

Почему ?Очень запутанно: MSDN (ссылка здесь) говорит, что возвращаемый массив должен иметь длину 2, а не 4.

Ответы [ 2 ]

1 голос
/ 19 августа 2011

ОК, я много гуглил и читал некоторые статьи на MSDN.Сейчас я делаю это так:

headerStruct sourceHeader;
// ... filling Header-Struct here ..

// First Create all arrays, for every element of the struct one Array
array< Byte >^ array1 = gcnew array< Byte >(sizeof(sourceHeader.number1));
array< Byte >^ array2 = gcnew array< Byte >(sizeof(sourceHeader.number2));
array< Byte >^ arrayName1 = gcnew array< Byte >(sizeof(sourceHeader.name1));
array< Byte >^ arrayName2 = gcnew array< Byte >(sizeof(sourceHeader.name2));
array< Byte >^ arraySize = gcnew array< Byte >(sizeof(sourceHeader.size));

// Let the Marshall Class copy those native Types (by casting there Pointers to a IntPtr)
Marshal::Copy( (IntPtr)(&sourceHeader.number1), array1, 0, sizeof(sourceHeader.number1) );
Marshal::Copy( (IntPtr)(&sourceHeader.number2), array2, 0, sizeof(sourceHeader.number2) );
Marshal::Copy( (IntPtr)(&sourceHeader.name1), arrayName1, 0, sizeof(sourceHeader.name1) );
Marshal::Copy( (IntPtr)(&sourceHeader.name2), arrayName2, 0, sizeof(sourceHeader.name2) );
Marshal::Copy( (IntPtr)(&sourceHeader.size), arraySize, 0, sizeof(sourceHeader.size) );

// Next we create the Array in Which all Data shall be copied alltogether
array<Byte>^ allInOne = gcnew array<Byte>(sizeof(headerStruct));
int allreadyCopied = 0;

// Copy those arrays into the "Big" Array (watch out for the right order!)
Array::Copy(array1, 0, allInOne, allreadyCopied, array1->Length);
allreadyCopied += array1->Length;
Array::Copy(array2, 0, allInOne, allreadyCopied, array2->Length);
allreadyCopied += array2->Length;
Array::Copy(arrayName1, 0, allInOne, allreadyCopied, arrayName1->Length);
allreadyCopied += arrayName1->Length;
Array::Copy(arrayName2, 0, allInOne, allreadyCopied, arrayName2->Length);
allreadyCopied += arrayName2->Length;
Array::Copy(arraySize, 0, allInOne, allreadyCopied, arraySize->Length);
allreadyCopied += arraySize->Length;

// Now let's Test if it worked correctly by converting back the ByteArray to a Struct
pin_ptr<unsigned char> p1 = &allInOne[0];
unsigned char* p2 = p1;
headerStruct myHeader = *reinterpret_cast<headerStruct*>(p2);

Работает на меня :) Надеюсь, что этот подход в порядке, как я это делаю.Я использовал информацию и примеры на следующих сайтах для этого решения:

  1. MSDN, статья 1
  2. MSDN, статья 2

Это должно работать для большинства пользовательских структур, использующих собственные типы, верно?Спасибо @ Hans Passant за то, что указал мне правильное направление:)

1 голос
/ 18 августа 2011

Вы попадаете в неприятности, когда я смешиваю нативный (неуправляемый) и управляемый типы.Sizeof (headerStruct) не работает, потому что вы забыли, что добавляете дополнительные байты после него.ArrayName1-> Length равно 4, потому что вы передаете нативный char в GetBytes ().Компилятор продвигает его до следующего совместимого управляемого типа, Int32.Только Char является 2 байтами, обратите внимание на заглавную C. Эквивалентно wchar_t в нативном коде.

Отправка таких двоичных данных по сети обычно является ошибкой.Вы надеетесь, что другой конец использует тот же тип компилятора и тот же самый порядок байтов.Что хорошо, когда вы контролируете оба конца, но звучит так, как будто вы этого не делаете.Универсальный формат работы везде - XML.Это тривиально в .NET, но не для нативного типа.Вам нужно написать оболочку для структуры, которая является классом ref и использует управляемые типы.Также причина, по которой у вас проблемы с классом маршала.

...