Visual c++ visualc&x2B+;(.NET 4.0)-序列化/将自定义结构复制到字节数组,以便通过NetworkStream发送

Visual c++ visualc&x2B+;(.NET 4.0)-序列化/将自定义结构复制到字节数组,以便通过NetworkStream发送,visual-c++,serialization,tcp,struct,c++-cli,Visual C++,Serialization,Tcp,Struct,C++ Cli,我正在与另一个人的程序进行TCP通信。为了正确通信,他为通过TCP发送的消息定义了一个头结构。它的定义如下: typedef struct { uint16_t number1; uint16_t number2; char name1[64]; char name2[64]; uint32_t size; } headerStruct; 结构是以某种方式填充的,最重要的是headerStruc

我正在与另一个人的程序进行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;
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);
但是

arrayName1->Length;         // = 4;

为什么?非常混乱:表示返回的数组的长度应该是2,而不是4。

您在混合本机(非托管)和托管类型时遇到了麻烦。sizeof(headerStruct)不起作用,因为您忘记了在它之后添加额外的字节。arrayName1->长度为4,因为您正在将本机字符传递给GetBytes()。编译器将其升级为下一个兼容的托管类型Int32。注意,只有一个字符是2个字节,大写字母C相当于本机代码中的wchar\u t


通过网络发送这样的二进制数据通常是错误的。您希望另一端使用相同类型的编译器和相同类型的endian。当你控制两端的时候,这很好,但听起来好像你没有。通用的works everywhere格式是XML。这在.NET中是微不足道的,但对于本机类型则不然。您必须为ref类的结构编写一个使用托管类型的包装器。这也是您在Marshal类中遇到问题的原因。

好的,我在谷歌上搜索了很多,并阅读了一些有关MSDN的文章。我现在是这样做的:

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;
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);
headerStruct sourceHeader;
// ... 在此处填充标题结构。。
//首先为struct-one数组的每个元素创建所有数组
array^array1=gcnewarray(sizeof(sourceHeader.number1));
array^array2=gcnewarray(sizeof(sourceHeader.number2));
数组^arrayName1=gcnew数组(sizeof(sourceHeader.name1));
数组^arrayName2=gcnew数组(sizeof(sourceHeader.name2));
array^arraySize=gcnew array(sizeof(sourceHeader.size));
//让Marshall类复制这些本机类型(通过将这些类型的指针转换为IntPtr)
封送:复制((IntPtr)(&sourceHeader.number1),数组1,0,sizeof(sourceHeader.number1));
Marshal::Copy((IntPtr)(&sourceHeader.number2),array2,0,sizeof(sourceHeader.number2));
封送:复制((IntPtr)(&sourceHeader.name1),arrayName1,0,sizeof(sourceHeader.name1));
封送:复制((IntPtr)(&sourceHeader.name2),arrayName2,0,sizeof(sourceHeader.name2));
Marshal::Copy((IntPtr)(&sourceHeader.size),arraySize,0,sizeof(sourceHeader.size));
//接下来,我们创建一个数组,其中所有数据都将一起复制
数组^allInOne=gcnew数组(sizeof(headerStruct));
int-allreadyCopied=0;
//将这些数组复制到“大”数组中(注意正确的顺序!)
Array::Copy(array1,0,allInOne,allreadyCopied,array1->Length);
allreadyCopied+=array1->长度;
Array::Copy(array2,0,allInOne,allreadyCopied,array2->Length);
allreadyCopied+=array2->长度;
数组::复制(arrayName1,0,allInOne,allreadyCopied,arrayName1->Length);
allreadyCopied+=arrayName1->长度;
数组::复制(arrayName2,0,allInOne,allreadyCopied,arrayName2->Length);
allreadyCopied+=arrayName2->长度;
数组::复制(arraySize,0,allInOne,allreadyCopied,arraySize->Length);
allreadyCopied+=阵列大小->长度;
//现在,让我们通过将ByteArray转换回结构来测试它是否正常工作
pin_ptr p1=&allInOne[0];
无符号字符*p2=p1;
headerStruct myHeader=*重新解释铸件(p2);
对我有效:)希望这种方法是好的,我这样做。 我在以下网站上使用了此解决方案的信息和示例:


  • 这应该适用于大多数使用本机类型的自定义结构,对吗?谢谢@Hans Passant为我指明了正确的方向:)

    谢谢你的帮助,Hans!通信肯定需要按照另一个人在他的程序中定义的方式来完成,这意味着:我必须发送一条消息,该消息由一个大小恒定的头结构和一个大小可变的消息体组成。你有关于如何编写包装类的链接吗?当我发送的字节数组必须看起来像我使用的本机类型时,我不知何故不知道如何使用托管类型。当另一个人坚持要写包装时,写包装就没有意义了。只需使用BinaryWriter。