2011-08-18 13 views
1

我正在与另一个人的程序进行TCP通信。为了正确的通信,他为通过TCP发送的消息定义了Header-Struct。它被定义的东西如下:Visual C++(.NET 4.0) - 序列化/将自定义结构体复制到字节阵列以通过NetworkStream发送

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)代替的硬编码值为大小如果结果数组。我只是用变量allreadyCopied“计算”它。但他们为什么不同?我的错误在哪里?

  2. 这种方法肯定不是很优雅。有一个更好的方法吗?我试过了元帅级,但没有设法让它工作!

  3. 不知何故,感觉这种方法是错误的。这是正确的方式还是我在这里完全错误?

任何帮助感激很多!谢谢!

编辑:

OK刚刚检查:

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

arrayName1->Length;   // = 4; 

为什么?非常容易混淆:MSDN (link here)表示返回的Array应该是长度为2的,而不是4.

回答

1

好的,我搜索了很多并阅读了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 Article 1
  2. MSDN Article 2

这应该对大多数定制的Structs使用原生类型,工作的权利?感谢@ Hans Passant指点我进入正确的方向:)

1

您让自己陷入了混淆本机(非托管)和托管类型的困境。 sizeof(headerStruct)不起作用,因为您忘记了在其后添加额外的字节。 arrayName1-> Length是4,因为您将本地char传递给GetBytes()。编译器将它提升到下一个兼容的托管类型Int32。只有Char是2个字节,请注意大写C.等同于本机代码中的wchar_t。

通过网络发送像这样的二进制数据通常是一个错误。你希望另一端使用相同类型的编译器和相同类型的字节顺序。当你控制两端时,这很好,但听起来像你不这样做。一个通用的作品无处不在的格式是XML。这在.NET中并不重要,但不适用于本机类型。你必须为ref类的结构编写一个包装器,并使用托管类型。这也是你为什么遇到元帅班的原因。

+0

感谢您的帮助Hans!通信肯定需要按照其他人在他的程序中定义它的方式来完成,这意味着:我必须发送包含具有常量大小的Header-Struct的消息,直接跟随具有可变大小的消息主体。你有什么链接怎么写Wrapper Class?我不知道如何使用托管类型时,我发送的字节数组必须看起来像我使用本机类型。 – EliteTUM

+0

当其他人坚持写封包时没有意义。只需使用BinaryWriter。 –