Заголовок с ограничениями, использующими байты для сокета UDP - PullRequest
0 голосов
/ 27 сентября 2018

Я делаю заголовок для сокета UDP, который имеет ограничения, использующие байты.

|Идентификатор пакета (1 байт) |Размер пакета (2 байта) |ID подпакета (1 байт) |etc

Я создал структуру для хранения таких атрибутов, как:

typedef struct WHEATHER_STRUCT
{
   unsigned char packetID[1];
   unsigned char packetSize[2];
   unsigned char subPacketID[1];
   unsigned char subPacketOffset[2];
   ...
} wheather_struct;

Я инициализировал эту структуру, используя новые, и обновил значения.Вопрос в том, хочу ли я использовать только 2 байта в атрибуте размера пакета.Какая из этих двух форм, которые я написал ниже, является правильной?

*weather_struct->packetSize = '50';

или

*weather_struct->packetSize = 50;

Ответы [ 3 ]

0 голосов
/ 27 сентября 2018

Если вы можете использовать C ++ 11 и gcc (или clang), я бы сделал следующее:

typedef struct WHEATHER_STRUCT
{
   uint8_t packetID;
   uint16_t packetSize;
   uint8_t subPacketID;
   uint16_t subPacketOffset;
   // ...
}  __attribute__((packed)) wheather_struct;

Если вы не можете использовать C ++ 11, тогда вы можете использовать unsigned char иunsigned short вместо.

Если вы используете Visual C, вы можете сделать:

#pragma pack (push, 1)
typedef struct ...
#pragma (pop)

Остерегайтесь также проблем с порядком байтов, в зависимости от того, какие архитектуры вам нужно поддерживать.Вы можете использовать htons() и ntohs() для решения этой проблемы.

Демонстрация в реальном времени на Wandbox

0 голосов
/ 27 сентября 2018

Упаковка и распаковка данных из IP-пакетов - такая же старая проблема, как и сам Интернет (на самом деле, более старый).

Различные архитектуры компьютеров имеют разные схемы представления целых чисел, что может вызвать проблемы при обмене данными между компьютерами.

По этой причине стек IP стандартизируется для кодирования целых чисел в «порядке байтов сети» (что в основном означает самый старший байт первым).

Существуют стандартные функции для преобразования значений в порядке байтов сети внативные типы и наоборот.Я настоятельно рекомендую вам использовать их, так как ваш код будет более переносимым.

Кроме того, имеет смысл абстрагировать представления данных с точки зрения программы.Компиляторы c ++ могут выполнять преобразования очень эффективно.

Пример:

#include <arpa/inet.h>
#include <cstring>
#include <cstdint>

typedef struct WEATHER_STRUCT
{
   std::int8_t packetID;
   std::uint16_t packetSize;
   std::uint8_t subPacketID;
   std::uint16_t subPacketOffset;
} weather_struct;


const std::int8_t* populate(weather_struct& target, const std::int8_t* source)
{
    auto get16 = [&source]
    {
        std::uint16_t buf16;
        std::memcpy(&buf16, source, 2);
        source += 2;
        return ntohs(buf16);
    };
    target.packetID = *source++;
    target.packetSize = get16();
    target.subPacketID = *source++;
    target.subPacketOffset = get16();
    return source;
}

uint8_t* serialise(uint8_t* target, weather_struct const& source)
{
    auto write16 = [&target](std::uint16_t val)
    {
        val = ntohs(val);
        std::memcpy(target, &val, 2);
        target += 2;
    };

    *target++ = source.packetID;
    write16(source.packetSize);
    *target++ = source.subPacketID;
    write16(source.subPacketOffset);
    return target;
}

https://linux.die.net/man/3/htons

вот ссылка на версию C ++ 17 выше:

https://godbolt.org/z/oRASjI

Еще одно примечание о стоимости преобразования:

Данные, поступающие в вашу программу или покидающие ее, - это событие, которое происходит один раз для каждой полезной нагрузки.Принятие затрат на конвертацию здесь влечет за собой незначительное наказание.

Как только данные поступили в вашу программу или до того, как они ушли, они могут многократно манипулироваться вашим кодом.

Некоторые архитектуры процессоров страдаютогромные потери производительности при доступе к данным, если данные не выровнены по естественным границам слова.Вот почему существуют такие атрибуты, как packed - компилятор делает все возможное, чтобы избежать смещения данных.Использование упакованного атрибута равносильно преднамеренному указанию компилятору создавать очень неоптимальный код.

По этой причине я бы рекомендовал не использовать упакованные структуры (например, __attribute__((packed)) и т. Д.) Для данных, на которые ссылается программалогика.

По сравнению с ОЗУ сети на много порядков медленнее.Незначительное снижение производительности (буквально наносекунды) в точке кодирования или декодирования сетевого пакета несущественно по сравнению со стоимостью его фактической передачи.

Структуры упаковки могут вызвать ужасные проблемы с производительностью в программном коде и часто приводят к переносимостиголовные боли.

0 голосов
/ 27 сентября 2018

Неправильно, вам нужно рассматривать два байта как одно 16-битное число.Вы, вероятно, также должны принять во внимание различные порядковые номера сетевого потока в зависимости от архитектуры вашего процессора (в зависимости от протокола большинство из них имеют старший порядковый номер).

Поэтому правильный код будет:

*((uint16_t*)weather_struct->packetSize) = htons(50);

Было бы проще, если бы packetSize были uint16_t для начала:

weather_struct->packetSize = htons(50);
...