将缓冲区复制到struct时丢失数据

kar*_*kar 0 c++ struct

我有一个TCP服务器套接字,它接收一个16字节的请求消息.请求消息将具有多个字段,并且基于我需要进行不同操作的字段值.

当我尝试将缓冲区复制到struct时,我可以看到丢失的数据.我尝试了所有可能的方法,但无法弄清楚我是否需​​要进行结构填充.

我的结构看起来像,

struct stRequestMsg {
    uint16_t startTag;
    uint32_t messageSize;
    uint16_t messageID;
    uint16_t sequenceNumber;
    uint16_t messageType;
    uint32_t reserved;
};
Run Code Online (Sandbox Code Playgroud)

我所做的就是,

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

    }
Run Code Online (Sandbox Code Playgroud)

我可以看到接收到的值buff是正确的,但是当我试图将整个缓冲区映射到struct时,我可以看到丢失的数据.我确信解决方案可能很简单,但我不知道问题出在哪里.

输出看起来像,

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
Run Code Online (Sandbox Code Playgroud)

但我的期望是

startTag = 0x5000;
messageSize = 0x1E000000;
messageID = 0x3100;
sequenceNumber = 0x100;
messageType = 0x200;
reserved = 0x1000000;
Run Code Online (Sandbox Code Playgroud)

编辑:我尝试更改messageSize的变量大小并保留到uint16_t,并通过memcpy我收到所有正确的.

Dan*_*our 6

50 00 1E 00 00 00 31 00 01 00 02 00 01 00 00 00
Run Code Online (Sandbox Code Playgroud)

如果我们相信您的第一个输出,那么这就是缓冲区中的内容.

struct stRequestMsg {
    uint16_t startTag;
    uint32_t messageSize;
    uint16_t messageID;
    uint16_t sequenceNumber;
    uint16_t messageType;
    uint32_t reserved;
};
Run Code Online (Sandbox Code Playgroud)

你似乎假设这个结构定义像这样映射到内存:

50 00|1E 00 00 00|31 00|01 00|02 00|01 00 00 00
start|messagesize|msgID|seqNm|msgTp|reserved...
Run Code Online (Sandbox Code Playgroud)

在你的特殊情况下,情况并非如此

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...
Run Code Online (Sandbox Code Playgroud)

哪里GG意味着垃圾(因为它在你的缓冲区之外).在内存中结构如此布局的原因是结构填充:结构的字段根据它们的对齐要求布置在存储器中.对于int(并且可能uint32_t)通常是4字节,意味着这样的成员将仅以4的倍数的偏移量开始.为了实现这一点,编译器插入填充.因此,您的结构看起来像:

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;
};
Run Code Online (Sandbox Code Playgroud)

您遇到的另一个问题是字节顺序:您的系统似乎使用小端,而来自网络的数据将变为大端(这也是网络字节顺序,因此这是一件好事).

这就是为什么startTag == 0x0050而不是0x5000.作为小端,您的系统假设第一个字节保持最低位,而不是最高位.

为了做到这一点,你应该查看序列化序列化的一些资源......


反序列化示例(在C中,但应该可以很容易地调整到C++.对不起,在编写时没注意标记,我看到了一个printf只是假设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;
}
Run Code Online (Sandbox Code Playgroud)

(活在ideone上)