Отправка логических значений через Интернет (Windows & Linux) - PullRequest
2 голосов
/ 30 сентября 2011

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

#pragma pack(push, 1)

struct other_data_struct
{
int hp;
int wp;
char movetype;
}

struct PlayerStats
{
int playerID;
other_data_struct data;
bool friendly;    //<-this one messes up how the others are going to be packed on the 2 systems
}
#pragma pack(pop)

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

Логическое значение не работает, хотя, когда сервер скомпилирован с gcc для Linux, а клиент скомпилирован с MSVC для Windows ...

Я думал о создании какого-либо контейнера (то есть беззнакового символа с 8 логическими функциями get / set или аналогичными), но он кажется таким же изворотливым, как и не элегантным.

Есть ли какой-нибудь способ «упаковать» структуры, содержащие булевы переменные, точно такие же в Windows и Linux, или я должен кусать кислое яблоко и использовать символ для каждого булева?

Ответы [ 6 ]

2 голосов
/ 30 сентября 2011

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

ЭтоЯ часто так поступаю, работая на встраиваемых системах, где 1) память (как ОЗУ, так и исполняемое ПЗУ) является ценным ресурсом, поэтому следует избегать ненужных библиотек и 2) я знаю, что мой код предназначен только для одной платформыи 3) не хотят испытывать трудности при упаковке / распаковке.Даже тогда, вероятно, это не «лучшая» практика.

Если вы знаете о рисках, связанных с такими действиями, я думаю, что вы ответили на этот вопрос самостоятельно.Определите свой собственный тип для логического значения, если вы хотите быть на 100% уверены.Также помните, что размер long отличается между 32-битной и 64-битной платформой.Я обычно придерживаюсь типов "stdint.h" для каждого числового примитива, чтобы вы знали, с каким размером вы имеете дело.

2 голосов
/ 30 сентября 2011

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

2 голосов
/ 30 сентября 2011

Вы можете использовать буфер протокола Google вместо ваших собственных упакованных структур. Или вы можете отказаться от bool, чтобы избежать несовместимости.

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

1 голос
/ 30 сентября 2011

Мне не ясно, в чем проблема.И Windows, и Linux определяют bool как однобайтовый объект.

Windows определяет , определяет BOOL как int, поэтому, если вы ставите BOOL напровод и чтение его как bool (или добавление bool на провод и чтение его как BOOL), тогда у вас возникнут проблемы.Это относительно легко исправить.

Возможно, Windows и Linux определяют разные значения для false и true (более вероятно, что они согласны с тем, что false равно 0, но не согласныдля значения, используемого для true, даже вне сетевого программирования возможно иметь bool переменные, которые не являются true или false, см. ниже).Стандарт требует, чтобы bool s был хотя бы одним байтом, а байт имеет гораздо больше двух возможных значений.

Да, преобразование ваших bool s в unsigned char s и отправка , что по проводам будет работать нормально.


Примечание : "bool переменные, которые не являются true или false":

// obviously this code won't show up in anybody's code base
char a = 'a';
bool b = *reinterpret_cast<bool*>(&a);
switch (b) {
    case true:
       std::printf("true\n");
       break;
    case false:
       std::printf("false\n");
       break;
    default:
       std::printf("huh?\n");
       break;
 }
1 голос
/ 30 сентября 2011

Вы можете попробовать битовые поля , таким образом ваша упакованная структура содержит 'int', но ваш код использует более компактное представление.

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

1 голос
/ 30 сентября 2011

Почему бы вам не выполнить сериализацию при вызове ntohl, htonl, ntohs, htons и т. Д. Это преобразует штраф в порядке байтов - и это немного безопаснее, чем вы делаете.Если вы используете структуры, вам нужно больше беспокоиться о зависимых от компиляторах вещах, чем если вы используете базовые типы известных размеров.устройства.Другие сетевые программисты будут лучше понимать ваш код в целях обслуживания:)

...