将缓冲区复制到结构时丢失数据 [英] Missing data while copying buffer to struct

查看:47
本文介绍了将缓冲区复制到结构时丢失数据的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

我有一个接收 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;
}

(在 ideone 上直播)

这篇关于将缓冲区复制到结构时丢失数据的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆