Создание и использование кроссплатформенной структуры в C ++ - PullRequest
7 голосов
/ 18 ноября 2010

Я пишу кроссплатформенную игру с сетевыми возможностями (используя SFML и RakNet ), и я пришел к тому, что я скомпилировал сервер на своем сервере Ubuntu и запустил клиент на моем Mac , Вся разработка выполняется на моем Mac, поэтому я сначала тестировал сервер на этом, и он работал нормально.

Я отправляю struct s по сети, а затем просто возвращаю их из char * в (например) inet::PlayerAdded. Теперь это работает нормально (по большей части), но мой вопрос: это всегда будет работать? Это кажется очень хрупким подходом. Будет ли структура всегда выкладываться одинаково даже на других платформах, например, в Windows? Что бы вы порекомендовали?

#pragma pack(push, 1)
struct Player
{
    int dir[2];
    int left;
    float depth;
    float elevation;
    float velocity[2];
    char character[50];
    char username[50];
};

// I have been added to the game and my ID is back
struct PlayerAdded: Packet
{
    id_type id;
    Player player;
};
#pragma pack(pop)

Ответы [ 3 ]

9 голосов
/ 18 ноября 2010

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

Это также может привести к сбою, если выравнивание или упаковка вашей конструкции изменяется от машины к машине. Что если у вас есть 64-битные машины, а некоторые 32-битные?

Вам необходимо использовать подходящую переносимую библиотеку сериализации, такую ​​как Boost.Serialization или Буферы протокола Google , чтобы убедиться, что у вас есть проводной протокол (он же формат передачи данных), который можно успешно декодировать не зависит от аппаратного обеспечения.

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

5 голосов
/ 18 ноября 2010

Как и многие другие ответы, я бы посоветовал не отправлять необработанные двоичные данные, если этого можно избежать.Что-то вроде Boost serial или Google Protobuf отлично справится с работой без особых накладных расходов.

Но вы, безусловно, можете создавать кросс-платформенные двоичные структуры, это делается постоянно и является очень правильным способом обмена данными.Слои "структуры" над этими данными просто имеет смысл.Однако вы должны быть очень осторожны с компоновкой, к счастью, большинство компиляторов предоставляют вам множество возможностей сделать это.«pack» - одна из таких опций, которая много заботится.

Вам также нужно позаботиться о размерах данных.Просто включите stdint.h и используйте типы фиксированного размера, такие как uint32_t.Будьте осторожны со значениями с плавающей запятой, поскольку не все архитектуры будут иметь одно и то же значение, для 32-разрядных операций с плавающей запятой они, вероятно, имеют.Также для поддержки endaness большинство архитектур будет использовать то же самое, и если они этого не сделают, вы можете просто перевернуть его на клиенте, который отличается.

2 голосов
/ 18 ноября 2010

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

Различные операционные системы (даже на одной аппаратной платформе) могут использовать разные представления данных; обычно это называется «платформа ABI» и отличается, например, между 32-битная / 64-битная Windows, 32-битная / 64-битная Linux, MacOSX.

'# pragma pack' - это только половина пути, потому что за пределами ограничений на выравнивание могут быть различия в размере типов данных. Например, «long» в 64-битной Windows - 32-битная, в то время как в Linux и MacOSX - 64-битная.

Тем не менее, проблема, очевидно, не нова и уже решалась в прошлом - стандарт удаленного вызова процедур (RPC) содержит механизмы для того, как определять структуры данных независимым от платформы способом и как кодировать / декодировать «буферы», представляющие эти структуры. Он называется «XDR» (внешнее представление данных). См. RFC1832. В процессе программирования это колесо было изобретено несколько раз; конвертируете ли вы в XML, работаете на низком уровне самостоятельно с XDR, используете google :: protobuf, boost или Qt :: Variant, есть из чего выбирать.

С чисто стороны реализации: для простоты просто предположим, что "unsigned int" везде 32-битное выравнивание на 32-битной границе; если вы можете закодировать все свои данные в виде массива 32-битных значений, то единственная проблема с экстернализацией, с которой вам придется иметь дело, - это endianness.

...