Visual C++ (.NET 4.0) - 将自定义结构序列化/复制到字节数组以通过 NetworkStream 发送

发布于 2024-11-30 06:47:58 字数 2666 浏览 1 评论 0原文

我正在与另一个人的程序进行 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)而不是硬编码值< strong>520 表示结果数组的大小。我只是用变量allreadyCopied“计算”它。但为什么它们不同呢?我的错误在哪里?

  2. 这个方法肯定不是很优雅。有更好的方法吗?我尝试了 Marshal-Class,但没有成功!

  3. 不知怎的,感觉这种方法是错误的。这是正确的方法还是我完全错了?

非常感谢任何帮助!谢谢!

编辑:

确定,刚刚检查:

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

但是

arrayName1->Length;         // = 4;

为什么?非常令人困惑: MSDN(此处链接) 说返回的数组应该是长度为 2,而不是 4。

I am working on a TCP-Communication with another person's program. For proper Communication he defined a Header-Struct for the Messages sent via TCP. It is defined something as following:

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

The struct is somehow filled, most importantly headerStruct.size sets the number of bytes following the header and is the size of the messages to be sent.

I now tried to serialize the header like this:

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;

Now this seems to work for me, BUT:

  1. I tried using sizeof(headerStruct) instead of the hardcoded value 520 for the size if the result-array. I just "calculated" it with the variable allreadyCopied. But why are they different? Where's my error here?

  2. This method is surely not very elegant. Is there a better way to do this? I tried the Marshal-Class but did not manage to get it working!

  3. Somehow it feels like this approach is wrong. Is this the right way or am I completely wrong here?

Any help is appreciated a lot! Thanks!

Edit:

OK just checked:

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

BUT

arrayName1->Length;         // = 4;

Why? Very confusing: MSDN (link here) says that the returned Array should be of length 2, not 4.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(2

守不住的情 2024-12-07 06:47:58

我混合本机(非托管)和托管类型会给自己带来麻烦。 sizeof(headerStruct) 不起作用,因为您忘记在其后面添加额外的字节。 arrayName1->Length 为 4,因为您将本机 char 传递给 GetBytes()。编译器将其提升为下一个兼容的托管类型 Int32。只有一个 Char 是 2 个字节,注意大写的 C。相当于本机代码中的 wchar_t。

通过网络发送这样的二进制数据通常是一个错误。您希望另一端使用相同类型的编译器和相同类型的字节序。当你控制两端时这很好,但听起来你没有。 XML 是一种通用的通用格式。这在 .NET 中很简单,但对于本机类型则不然。您必须为作为引用类并使用托管类型的结构编写一个包装器。这也是你在元帅级遇到麻烦的原因。

You are getting yourself into trouble my mixing native (unmanaged) and managed types. The sizeof(headerStruct) doesn't work because you forgot that you add extra bytes after it. The arrayName1->Length is 4 because you are passing a native char to GetBytes(). The compiler promotes it to the next compatible managed type, Int32. Only a Char is 2 bytes, note the capital C. Equivalent to wchar_t in native code.

Sending binary data like this across a network is usually a mistake. You hope that the other end uses the same kind of compiler and same kind of endian-ness. Which is fine when you control both ends but it sounds like you don't. A generic works-everywhere format is XML. That's trivial in .NET but not for a native type. You'd have to write a wrapper for the struct that's a ref class and uses managed types. Also the reason why you have trouble with the Marshal class.

浅唱々樱花落 2024-12-07 06:47:58

好的,我在 google 上搜索了很多,并在 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 为我指明了正确的方向:)

OK, I googled a lot and read some articles on MSDN. I am now doing it like this:

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);

Works for me :) Hope this approach is OK the way I do it.
I used the Infos and Examples on following sites for this solution:

  1. MSDN Article 1
  2. MSDN Article 2

This should work for most Custom-Structs using native types, right? Thanks @ Hans Passant for pointing me into the right direction :)

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文