Каков наилучший способ отправки структур, содержащих значения перечисления через сокеты в C - PullRequest
3 голосов
/ 16 июня 2010

У меня есть множество различных структур, содержащих элементы enum, которые я должен передавать через TCP / IP.Хотя конечные точки связи находятся в разных операционных системах (Windows XP и Linux), что означает разные компиляторы (gcc 4.xx и MSVC 2008), обе части программы совместно используют одни и те же файлы заголовков с объявлениями типов.структуры должны передаваться напрямую (см. пример кода ниже) без дорогой сериализации или потоковой передачи членов внутри.

Таким образом, вопрос заключается в том, как обеспечить, чтобы оба компилятора использовали одно и то же представление внутренней памяти для элементов перечисления (т.е. оба использовали 32-разрядные целые числа без знака).Или, если есть лучший способ решить эту проблему ...

//type and enum declaration
typedef enum 
{
    A        = 1,
    B        = 2,
    C        = 3
} eParameter;


typedef enum
{
    READY    = 400,
    RUNNING  = 401,
    BLOCKED  = 402
    FINISHED = 403
} eState;


#pragma pack(push,1)
    typedef struct
    {
        eParameter mParameter;
        eState mState;
        int32_t miSomeValue;
        uint8_t miAnotherValue;
        ...
    } tStateMessage;
#pragma pack(pop)


//... send via socket
tStateMessage msg;
send(iSocketFD,(void*)(&msg),sizeof(tStateMessage));

//... receive message on the other side
tStateMessage msg_received;
recv(iSocketFD,(void*)(&msg_received),sizeof(tStateMessage));

Дополнительно ...

  • Так как обе конечные точки - это порядковые номера с прямым порядком байтов, порядок байтов здесь.
  • И пакет #pragma удовлетворительно решает проблемы с выравниванием.

Спасибо за ваши ответы, Аксель

Ответы [ 5 ]

1 голос
/ 16 июня 2010

Если вы не хотите проходить сериализацию, один из методов, которые я видел, используется для исключения перечислений и простого использования 32-битных беззнаковых целых чисел и #DEFINEs для эмуляции перечислений. Вы теряете некоторую безопасность типов для некоторых гарантий относительно формата данных.

В противном случае вы полагаетесь на то, что поведение, не гарантированное в спецификации языка, будет реализовано одинаково на всех ваших компиляторах. Если вас не беспокоит общая переносимость, и вы просто хотите обеспечить одинаковый эффект на двух компиляторах, это может быть возможно путем проб и ошибок и большого количества тестов, чтобы заставить эти два сделать одно и то же. Я считаю, что спецификация C99 позволяет внутренним перечислениям иметь размер int или меньше, но не больше int. Итак, одна вещь, которую я видел, чтобы предположительно намекнуть компилятору в правильном направлении:

typedef enum
{
    READY    = 400,
    RUNNING  = 401,
    BLOCKED  = 402,
    FINISHED = 403,
    MAX      = MAX_INT
} eState;

Это должно ограничить выбор компилятором того, как хранить перечисление. Обратите внимание, что компиляторы могут нарушать стандарт, однако я знаю, что gcc имеет нестандартную функцию, которая позволяет при необходимости использовать 64-битные перечисления.

Также проверьте: Каков размер перечисления в C?

1 голос
/ 16 июня 2010

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

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

Обратите внимание, что только Avro имеет официально поддерживаемый C API.Для Thrift и Protocol Buffers вы либо создаете тонкую оболочку C поверх API C ++, либо используете один из API C, например protobuf-c .

1 голос
/ 16 июня 2010

Это преждевременная оптимизация. Вы сделали два дорогостоящих предположения без измерений.

Первое предположение состоит в том, что эта часть кода является узким местом в производительности.Это?Очень маловероятно.Если кто-то будет делать предположения о производительности, то безопасным предположением будет то, что узким местом будет скорость сети, а не код, который отправляет и получает сетевые сообщения.Уже одно это должно помешать вам рассмотреть второе предположение.

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

Скептически?Измерь это!:)

1 голос
/ 16 июня 2010

Я отвечу на ваш вопрос прагматично, потому что вы выбрали относительно рискованный путь после сопоставления прироста производительности с возможными недостатками (по крайней мере, я надеюсь, что у вас есть!).

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

  1. Убедитесь, что вы используете инициализаторы для перечислений (ваши примеры делают это) во всех случаях.
  2. Выполните эмпирическое тестирование, чтобы увидеть, как данные интерпретируются на принимающей стороне.
  3. Запишите номера версий инструментов сборки с обеих сторон и заархивируйте их с исходным кодом.Также желательно архивировать инструменты.
  4. Документируйте все, что вы сделали, поэтому непредвиденное техническое обслуживание в будущем не помешает.
  5. Молитесь за лучшее!; -)
0 голосов
/ 16 июня 2010

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...