将缓冲区复制到结构时丢失数据 [英] Missing data while copying buffer to struct
问题描述
我有一个接收 16 字节请求消息的 TCP 服务器套接字.请求消息将有多个字段,并根据字段值我需要继续执行不同的操作.
I have a TCP server socket which receives a 16-byte request message. The request message will have several field and based on the field values i need to proceed with different actions.
当我尝试将缓冲区复制到结构体时,我可以看到丢失的数据.我尝试了所有可能的方法,但无法确定是否需要进行结构填充.
When I tried to copy the buffer to struct, I could see missing data. I tried all possible methods but couldn't able to figure out whether I need to do structure padding or not.
我的结构看起来像,
struct stRequestMsg {
uint16_t startTag;
uint32_t messageSize;
uint16_t messageID;
uint16_t sequenceNumber;
uint16_t messageType;
uint32_t reserved;
};
我所做的就是,
char buff[1024]
result = static_cast<int>(recv(sockDesc, buff, sizeof(stRequestMsg), 0));
if (0 < result) {
printf("\n Actual value on buffer");
for (int i = 0; i < result; i++)
{
printf("\n buff[%d] = 0x%x", i,buff[i]);
}
reqMessage = *(stRequestMsg *)buff;
printf("\n RESULT of reqMessage = *(stRequestMsg *)buff;");
printf("\nstartTag : 0x%x", reqMessage.startTag);
printf("\nmessageSize : 0x%x", reqMessage.messageSize);
printf("\nmessageID : 0x%x", reqMessage.messageID);
printf("\nsequenceNumber : 0x%x", reqMessage.sequenceNumber);
printf("\nmessageType : 0x%x", reqMessage.messageType);
printf("\nreserved : 0x%x", reqMessage.reserved);
stRequestMsg hdr;
std::copy(&hdr, &hdr + 1, reinterpret_cast<stRequestMsg*>(buff));
printf("\n RESULT of std::copy(&hdr, &hdr + 1, reinterpret_cast<stRequestMsg*>(buff));");
printf("\nstartTag : 0x%x", reqMessage.startTag);
printf("\nmessageSize : 0x%x", reqMessage.messageSize);
printf("\nmessageID : 0x%x", reqMessage.messageID);
printf("\nsequenceNumber : 0x%x", reqMessage.sequenceNumber);
printf("\nmessageType : 0x%x", reqMessage.messageType);
printf("\nreserved : 0x%x", reqMessage.reserved);
memcpy(&reqMessage, buff, sizeof(stRequestMsg));
printf("\n RESULT of std::copy(&hdr, &hdr + 1, reinterpret_cast<stRequestMsg*>(buff));");
printf("\nstartTag : 0x%x", reqMessage.startTag);
printf("\nmessageSize : 0x%x", reqMessage.messageSize);
printf("\nmessageID : 0x%x", reqMessage.messageID);
printf("\nsequenceNumber : 0x%x", reqMessage.sequenceNumber);
printf("\nmessageType : 0x%x", reqMessage.messageType);
printf("\nreserved : 0x%x", reqMessage.reserved);
reqMessage = *reinterpret_cast<stRequestMsg*>(buff);
printf("\n RESULT of reqMessage = *reinterpret_cast<stRequestMsg*>(buff);");
printf("\nstartTag : 0x%x", reqMessage.startTag);
printf("\nmessageSize : 0x%x", reqMessage.messageSize);
printf("\nmessageID : 0x%x", reqMessage.messageID);
printf("\nsequenceNumber : 0x%x", reqMessage.sequenceNumber);
printf("\nmessageType : 0x%x", reqMessage.messageType);
printf("\nreserved : 0x%x", reqMessage.reserved);
}
我可以看到在 buff
上接收到的值是正确的,但是当我尝试将整个缓冲区映射到结构时,我可以看到丢失的数据.我相信解决方案可能很简单,但我不知道问题出在哪里.
I could see the values that received on the buff
are to be correct, but when i tried to map the whole buffer to struct, I could see missing data. I am sure the solution may be simple, but I don't know where is the problem.
输出看起来像,
Actual value on buffer
buff[0] = 0x50
buff[1] = 0x0
buff[2] = 0x1e
buff[3] = 0x0
buff[4] = 0x0
buff[5] = 0x0
buff[6] = 0x31
buff[7] = 0x0
buff[8] = 0x1
buff[9] = 0x0
buff[10] = 0x2
buff[11] = 0x0
buff[12] = 0x1
buff[13] = 0x0
buff[14] = 0x0
buff[15] = 0x0
RESULT of reqMessage = *(stRequestMsg *)buff;
startTag : 0x50
messageSize : 0x310000
messageID : 0x1
sequenceNumber : 0x2
messageType : 0x1
reserved : 0x8a5c6da
RESULT of std::copy(&hdr, &hdr + 1, reinterpret_cast<stRequestMsg*>(buff));
startTag : 0x50
messageSize : 0x310000
messageID : 0x1
sequenceNumber : 0x2
messageType : 0x1
reserved : 0x8a5c6da
RESULT of memcpy(&reqMessage, buff, sizeof(stRequestMsg));
startTag : 0xf7ec
messageSize : 0x1e0050
messageID : 0x0
sequenceNumber : 0x31
messageType : 0x1
reserved : 0x1
RESULT of reqMessage = *reinterpret_cast<stRequestMsg*>(buff);
startTag : 0xf7ec
messageSize : 0x1e0050
messageID : 0x0
sequenceNumber : 0x31
messageType : 0x1
reserved : 0x1
但我期望的是
startTag = 0x5000;
messageSize = 0x1E000000;
messageID = 0x3100;
sequenceNumber = 0x100;
messageType = 0x200;
reserved = 0x1000000;
我尝试更改 messageSize 的可变大小并保留为 uint16_t 并且通过执行 memcpy
我收到了所有正确的信息.
I tried changing the variable size of messageSize and reserved to uint16_t and by doing memcpy
I received all correct.
推荐答案
50 00 1E 00 00 00 31 00 01 00 02 00 01 00 00 00
如果我们相信您的第一个输出,那么这就是您的缓冲区中的内容.
If we believe your first output, then this is what is in your buffer.
struct stRequestMsg {
uint16_t startTag;
uint32_t messageSize;
uint16_t messageID;
uint16_t sequenceNumber;
uint16_t messageType;
uint32_t reserved;
};
你似乎假设这个结构定义像这样映射到内存上:
You seem to be assuming that this structure definition maps like so onto memory:
50 00|1E 00 00 00|31 00|01 00|02 00|01 00 00 00
start|messagesize|msgID|seqNm|msgTp|reserved...
事实并非如此,在您的特定情况下是
Which is not the case, in your particular situation it's
50 00|1E 00|00 00 31 00|01 00|02 00|01 00|00 00|GG GG GG GG
start|xx xx|messagesize|msgID|seqNm|msgTp|xx xx|reserved...
其中 GG
表示 垃圾(因为它在您的缓冲区之外).结构在内存中这样布局的原因是结构填充:结构的字段根据它们的对齐要求在内存中布局.对于 int
(可能还有 uint32_t
),通常是 4 字节,这意味着这样的成员只会从 4 的倍数的偏移量开始.为了实现这一点,编译器插入了填充.因此,您的结构实际上看起来像:
Where GG
means garbage (as it's outside of your buffer). The reason the structure is laid out like this in memory is structure padding: Fields of structures are laid out in memory according to their alignment requirements. For int
(and probably uint32_t
) that's often 4 Bytes, meaning such a member will only start at an offset that is a multiple of 4. To achieve this the compiler inserts padding. Your structure thus actually looks like:
struct stRequestMsg {
uint16_t startTag;
char xxxxPad1[2];
uint32_t messageSize;
uint16_t messageID;
uint16_t sequenceNumber;
uint16_t messageType;
char xxxxPad2[2];
uint32_t reserved;
};
您遇到的另一个问题是字节顺序:您的系统似乎使用little endian,而来自网络的数据则大endian(这也是网络字节顺序,所以这是一件好事).
The other issue you're running into is byte order: Your system seems to use little endian, while the data from the network is coming as big endian (which is also the network byte order, so this is a good thing).
这就是 startTag == 0x0050
而不是 0x5000
的原因.作为小端,您的系统假定第一个字节包含最低位,而不是最高位.
That's why startTag == 0x0050
instead of 0x5000
. Being little endian, your system assumes the first byte to hold the lowest bits, not the highest.
要做到这一点,您应该查找有关序列化和反序列化的一些资源...
To get this right you should look up some resources on serialization and deserialization ...
反序列化示例(在 C 中,但应该很容易调整到 C++.抱歉,在编写时没有注意标签,我看到了一个 printf
并假设 C :D):
Deserialization example (in C, but should be easily adjustable to C++. Sorry, wasn't paying attention to the tags when writing, I saw a printf
and just assumed C :D):
#include <stdio.h>
#include <stdint.h>
struct stRequestMsg {
uint16_t startTag;
uint32_t messageSize;
uint16_t messageID;
uint16_t sequenceNumber;
uint16_t messageType;
uint32_t reserved;
};
void printIt(struct stRequestMsg m) {
printf("{\n"
" .startTag = %#x;\n"
" .messageSize = %#x;\n"
" .messageID = %#x;\n"
" .sequenceNumber = %#x;\n"
" .messageType = %#x;\n"
" .reserved = %#x;\n"
"}\n",
m.startTag, m.messageSize, m.messageID,
m.sequenceNumber, m.messageType, m.reserved);
}
uint16_t deserialize_uint16(char const * const b) {
return ((uint16_t) b[0] << 8u) |
((uint16_t) b[1]);
}
uint32_t deserialize_uint32(char const * const b) {
return ((uint16_t) b[0] << 24u) |
((uint16_t) b[1] << 16u) |
((uint16_t) b[2] << 8u) |
((uint16_t) b[3]);
}
struct stRequestMsg deserialize(char const * const b) {
struct stRequestMsg r;
r.startTag = deserialize_uint16(b);
r.messageSize = deserialize_uint32(b + 2);
r.messageID = deserialize_uint16(b + 6);
r.sequenceNumber = deserialize_uint16(b + 8);
r.messageType = deserialize_uint16(b + 10);
r.reserved = deserialize_uint32(b + 12);
return r;
}
int main(void) {
char buff[16];
buff[0] = 0x50; buff[1] = 0x00;
buff[2] = 0x1E; buff[3] = 0x00;
buff[4] = 0x00; buff[5] = 0x00;
buff[6] = 0x31; buff[7] = 0x00;
buff[8] = 0x10; buff[9] = 0x00;
buff[10] = 0x20; buff[11] = 0x00;
buff[12] = 0x10; buff[13] = 0x00;
buff[14] = 0x00; buff[15] = 0x00;
struct stRequestMsg msg = deserialize(&(buff[0]));
printIt(msg);
return 0;
}
这篇关于将缓冲区复制到结构时丢失数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!