Хотите перевести / типизировать части массива char в значения - PullRequest
1 голос
/ 21 августа 2011

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

По сути, я сделал макет пакета того, что я ожидаю, что мои пакеты будут выглядеть немного. По существу, Char (8-битное значение), указывающее, что представляет собой сообщение, и это обнаруживается оператором switch, который затем заполняет значения на основе данных после этого 8-битного значения. Я ожидаю, что в моем пакете есть все виды сообщений, которые могут быть не в порядке.

Например, у меня может закончиться сердцебиение в конце или строка текста из сообщения чата и т. Д.

Я просто хочу иметь возможность сказать своей программе, взять данные из определенной точки в массиве char и преобразовать тип (если это так?) В то, что я хочу, чтобы они были. Какой хороший простой способ сделать это?

char bufferIncoming[15];
ZeroMemory(bufferIncoming,15);

//Making a mock packet
bufferIncoming[0] = 0x01; //Heartbeat value
bufferIncoming[1] = 0x01; //Heartbeat again just cause I can
bufferIncoming[2] = 0x10; //This should = 16 if its just an 8bit number, 

bufferIncoming[3] = 0x00; // This
bufferIncoming[4] = 0x00; // and this
bufferIncoming[5] = 0x00; // and this
bufferIncoming[6] = 0x09; // and this should equal "9" of its is a 32bit number (int)

bufferIncoming[7] = 0x00;
bufferIncoming[8] = 0x00;
bufferIncoming[9] = 0x01;
bufferIncoming[10] = 0x00; //These 4 should be 256 I think when combines into an unsigned int
//End of mockup packet


int bufferSize = 15; //Just an arbitrary value for now
int i = 0;
while (i < bufferSize)
{
    switch (bufferIncoming[i])
    {
        case 0x01: //Heart Beat
            {
                cout << "Heartbeat ";
            }
            break;
        case 0x10: //Player Data
            {
                //We've detected the byte that indicates the following 8 bytes will be player data. In this case a X and Y position

                playerPosition.X = ??????????; //How do I combine the 4 hex values for this?
                playerPosition.Y = ??????????;
            }
            break;
        default:
            {
                cout << ".";
            }
            break;

    }
    i++;
}
cout << " End of Packet\n";


UPDATE

Следуя идее Клервуара, я добавил следующее.

playerPosition.X = long(bufferIncoming[3]) << 24 | long(bufferIncoming[4]) << 16 | long(bufferIncoming[5]) << 8 | long(bufferIncoming[6]);

Обратите внимание, что я изменил значения сдвига.

Еще одно важное изменение было

unsigned char bufferIncoming[15]

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

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

У меня такое чувство, что должен быть более быстрый и чистый способ делать то, что я хочу. Я пробовал что-то вроде ...

playerPosition.X = *(bufferIncoming + 4) //Only giving me the value of the one hex value, not the combined >_<
playerPosition.X = reinterpret_cast<unsigned long>(&bufferIncoming); //Some random number that I dont know what it was

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

Например, может быть ...

playerPosition = (playerPosition)bufferIncoming[5]; //Reads from this spot and fills in the 8 bytes worth of data
//or
playerPosition.Y = (playerPosition)bufferIncoming[9]; //Reads in the 4 bytes of values

... Почему это не работает, или что-то подобное?

Ответы [ 3 ]

1 голос
/ 21 августа 2011

Вероятно, есть симпатичная версия этого, но лично я бы скомбинировал четыре переменные типа char, используя сдвиги влево и / или как:

playerPosition.X = long(buffer[0]) | long(buffer[1])<<8 | long(buffer[2])<<16 | long(buffer[3])<<24;

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

Редактировать: Порядковый номер может стать фактором, зависящим от того, как отправляющая машина первоначально разбивает целое число перед отправкой его по сети. Если оно не разлагает целое число таким же образом, как для его перекомпоновки с использованием сдвигов, вы можете получить значение, в котором первый байт является последним, а последний байт первым. Подобные мелкие неоднозначности побуждают большинство использовать сетевые библиотеки, ага.

Пример разбиения целого числа с помощью побитового вида будет выглядеть примерно так

buffer[0] = integer&0xFF;
buffer[1] = (integer>>8)&0xFF;
buffer[2] = (integer>>16)&0xFF;
buffer[3] = (integer>>24)&0xFF;
0 голосов
/ 21 августа 2011

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

playerPosition.X = *(int*)(bufferIncoming+3);

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

Спасибо, ребята, за помощь:)

0 голосов
/ 21 августа 2011

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

struct Header
{
    unsigned char message_type_;
    unsigned long message_length_;
};

struct HeartBeat : public Header
{
    // no data, just a heartbeat
};

struct PlayerData : public Header
{
    unsigned long position_x_;
    unsigned long position_y_;
};

unsigned char* raw_message; // filled elsewhere

// reinterpret_cast is usually best avoided, however in this particular
// case we are casting two completely unrelated types and is therefore
// necessary
Header* h = reinterpret_cast<Header*>(raw_message);

switch(h)
{
    case HeartBeat_MessageType:
    break;

    case PlayerData_MessageType:
    {
        PlayerData* data = reinterpret_cast<PlayerData*>(h);
    }
    break;
}
...