C ++: заполнить структуру данными из буфера - PullRequest
0 голосов
/ 22 октября 2018

Мне было интересно, могу ли я дать некоторые рекомендации о том, как взять данные из буфера и загрузить их в структуру.Например, я имею дело с буфером ответов DNS.Мне нужно заполнить структуру ответа DNS, чтобы я мог интерпретировать данные.Пока у меня есть следующее:

int DugHelp::getPacket() {
    memset(buf, 0, 2000); // clearing the buffer to make sure no "garbage" is there
    if (( n = read(sock, buf, 2000)) < 0 {
        exit(-1);
    }
    // trying to populate the DNS_Answers struct
    dnsAnswer = (struct DNS_Answer *) & buf;
    . . .
} 

Это структура, которую я определил:

struct DNS_Answer{
    unsigned char name [255];
    struct {
        unsigned short type;
        unsigned short _class;
        unsigned int ttl;
        unsigned in len;
    } types;
    unsigned char data [2000];
};

Ответы [ 2 ]

0 голосов
/ 22 октября 2018

Я делаю что-то вроде этого (довольно непроверенного) кода:

Код библиотеки:

namespace net {

using byte = unsigned char;

enum class endian
{
#ifdef _WIN32
    little = 0,
    big    = 1,
    native = little
#else
    little  = __ORDER_LITTLE_ENDIAN__,
    big     = __ORDER_BIG_ENDIAN__,
    native  = __BYTE_ORDER__,
#endif
};

constexpr bool is_little_endian()
{
    return endian::native == endian::little;
}

template<typename POD>
byte* write_to_buffer(POD const& pod, byte* pos)
{
    if(is_little_endian())
        std::reverse_copy((byte*)&pod, (byte*)& pod + sizeof(pod), pos);
    else
        std::copy((byte*)&pod, (byte*)& pod + sizeof(pod), pos);

    return pos + sizeof(pod);
}

template<typename POD>
byte const* read_from_buffer(byte const* pos, POD& pod)
{
    if(is_little_endian())
        std::copy(pos, pos + sizeof(pod), (byte*)&pod);
    else
        std::reverse_copy(pos, pos + sizeof(pod), (byte*)&pod);

    return pos + sizeof(pod);
}

} // namespace net

Код приложения:

struct DNS_Answer{
    unsigned char name [255];
    struct {
        unsigned short type;
        unsigned short _class;
        unsigned int ttl;
        unsigned int len;
    } types;
    unsigned char data [2000];
};

net::byte* write_to_buffer(DNS_Answer const& ans, net::byte* buf)
{
    auto pos = buf;
    pos = net::write_to_buffer(ans.name, pos);
    pos = net::write_to_buffer(ans.types.type, pos);
    pos = net::write_to_buffer(ans.types._class, pos);
    pos = net::write_to_buffer(ans.types.ttl, pos);
    pos = net::write_to_buffer(ans.types.len, pos);
    pos = net::write_to_buffer(ans.data, pos);
    return pos;
}

net::byte const* read_from_buffer(net::byte const* buf, DNS_Answer& ans)
{
    auto pos = buf;
    pos = net::read_from_buffer(pos, ans.name);
    pos = net::read_from_buffer(pos, ans.types.type);
    pos = net::read_from_buffer(pos, ans.types._class);
    pos = net::read_from_buffer(pos, ans.types.ttl);
    pos = net::read_from_buffer(pos, ans.types.len);
    pos = net::read_from_buffer(pos, ans.data);

    return pos;
}

Он должен быть довольно переносимым, иметь дело с различными порядками байтов и избегать потенциальных проблем с выравниванием.Вы также можете переносить типы, не относящиеся к стручкам, разбивая их на несколько частей POD и отправляя их отдельно.Например, std::string можно отправить как std::size_t для длины, а остальные как массив символов .

0 голосов
/ 22 октября 2018

Зависит от формата данных buf.Если формат совпадает с DNS_Answer.Вы можете использовать memcpy .Если их форматы одинаковы, вы должны сначала выровнять байты .

#pragma pack (1)
struct DNS_Answer{
    unsigned char name [255];
    struct {
        unsigned short type;
        unsigned short _class;
        unsigned int ttl;
        unsigned in len;
    } types;
    unsigned char data [2000];
};
#pragma pop(1)

Затем

memcpy(dnsAnswer, buf, sizeof(DNS_Answer));

Если их форматы данных не совпадают, у вас естьчтобы проанализировать их самостоятельно, или вы можете использовать DFDL (язык описания формата данных.)

...