2013-05-18 5 views
0

Я написал серверное приложение на C++, которое получает пакет данных из клиентского приложения (которое я сам не написал) и выводит данные на консоль. Проблема заключается в том, что когда я пытаюсь получить и сохранить весь пакет сразу, данные хранятся некорректно, однако, когда тело пакета принимается и сохраняется с помощью нескольких вызовов recv(), оно хранится правильно.Получение и хранение данных в C++

Что касается контентоспособности, клиент и сервер работают на маленькой конечной машине, клиент отправляет данные как минимальные, а сервер читает их без необходимости преобразования.

Это пакет, который клиентское приложение отправляет к серверному приложению:

00 03 23 00 57 6f 57 00 01 0c 01 f3 16 36 38 78 
00 6e 69 57 00 42 47 6e 65 00 00 00 00 7f 00 00 
01 05 41 44 4d 49 4e 

Вот структурированный вид пакета:

cmd    00 
error   03 
pkt_size  23 00 
gamename  57 6f 57 00 
version1  01 
version2  0c 
version3  01 
build   f3 16 
platform  36 38 78 00 
os    6e 69 57 00 
country   42 47 6e 65 
timezone_bias 00 00 00 00 
ip    7f 00 00 01 
srp_I_len  05 
srp_I   41 44 4d 49 4e 

Эти ожидаемые результаты, которые должны быть распечатаны приложением сервера:

cmd:   0 
error:   3 
pkt_size:  35 
gamename:  5730135 
version1:  1 
version2:  12 
version3:  1 
build:   5875 
platform:  7878710 
os:    5728622 
country:  1701726018 
timezone_bias: 0 
ip:    127 0 0 1 
srp_I_len:  5 
srp_I:   ADMIN 

Вот код, который у меня возникли проблемы с:

struct packet{ 
    uint8 cmd; 
    uint8 error; 
    uint16 pkt_size; 
    uint32 gamename; 
    uint8 version1; 
    uint8 version2; 
    uint8 version3; 
    uint16 build; 
    uint32 platform; 
    uint32 os; 
    uint32 country; 
    uint32 timezone_bias; 
    uint8 ip[4]; 
    uint8 srp_I_len; 
    uint8 srp_I[16]; 
}; 

packet data; 
recv(clientSocket, &data.cmd, 4, 0); // Receive packet header 
recv(clientSocket, &data.gamename, 46, 0); // Receive packet body 

printf("%d\n", data.cmd); 
... 
printf("%s\n", data.srp_i); 

Результат:

cmd:   0 
error:   3 
pkt_size:  35 
gamename:  5730135 
version1:  1 
version2:  12 
version3:  1 
build:   13846 (this is where it all goes wrong) 
platform:  1466527232 
os:    1850163712 
country:  101 
timezone_bias: 35512 
ip:    1 5 65 68 
srp_I_len:  77 
srp_I:   IN 

Если изменить код так:

recv(clientSocket, &data.cmd, 4, 0); // Receive packet header 
recv(clientSocket, &data.gamename, 7, 0); // Receive packet body 
recv(clientSocket, &data.build, 39, 0); // Receive packet body 

Результат:

... same expected results 
build:   5875 (fixed) 
platform:  1768816760 (goes all wrong here instead) 
os:    1195507799 
country:  25966 
timezone_bias: 8323072 
ip:    0 1 5 65 
srp_I_len:  68 
srp_I:   MIN 

И если я сделать последнюю корректировку кода, например, так:

recv(clientSocket, &data.cmd, 4, 0); // Receive packet header 
recv(clientSocket, &data.gamename, 7, 0); // Receive packet body 
recv(clientSocket, &data.build, 2, 0); // Receive packet body 
recv(clientSocket, &data.platform, 37, 0); // Receive packet body 

Результат:

... same expected results 
build:   5875 
platform:  7878710 
os:    5728622 
country:  1701726018 
timezone_bias: 0 
ip:    127 0 0 1 
srp_I_len:  5 
srp_I:   ADMIN 

Вызов recv() multiple раз, он получил и сохраняет данные точно так, как ожидалось. Я абсолютно не знаю, почему данные хранятся некорректно, только дважды вызывая recv(). Пожалуйста, кто-нибудь, просветите меня. Спасибо.

PS: Извините за пост монстра уродства.

+0

Компилятор C оставил пробелы в вашем 'struct packet' по соображениям оптимизации. Однако пакет, отправленный на проводе, не имел пробелов. Если бы я делал это, я бы «recv» пакет в буфере временного байта, а затем вручную считывал каждое поле из буфера и записывал его в мою собственную структуру, избегая всех проблем выравнивания структуры. Ваш код также принимает общее соответствие с удаленным хостом. – NovaDenizen

ответ

1

Заявить структуру как упакованную структуру, чтобы избежать проблем с выравниванием;

На окнах используют #pragma pack (1) (см msdn)

На GCC использование __attribute__((packed))

, чтобы устранить проблемы выравнивания. На самом деле gcc будет поддерживать прагмы стиля Windows для совместимости. Посмотри:

http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html

EDIT

Так пример код показан один из упакованной структуры внутри другие нетронутых структур:

При компиляции на x86_64-битной платформе:

#include <iostream> 
#include <stdint.h> 
#include <string.h> 

using namespace std; 


uint8_t input[] = { 
    0x00, 0x03, 0x23, 0x00, 0x57, 0x6f, 0x57, 0x00, 
    0x01, 0x0c, 0x01, 0xf3, 0x16, 0x36, 0x38, 0x78, 
    0x00, 0x6e, 0x69, 0x57, 0x00, 0x42, 0x47, 0x6e, 
    0x65, 0x00, 0x00, 0x00, 0x00, 0x7f, 0x00, 0x00, 
    0x01, 0x05, 0x41, 0x44, 0x4d, 0x49, 0x4e 
}; 

struct __attribute__((packed)) packet{ 
    uint8_t cmd; 
    uint8_t error; 
    uint16_t pkt_size; 
    uint32_t gamename; 
    uint8_t version1; 
    uint8_t version2; 
    uint8_t version3; 
    uint16_t build; 
    uint32_t platform; 
    uint32_t os; 
    uint32_t country; 
    uint32_t timezone_bias; 
    uint8_t ip[4]; 
    uint8_t srp_I_len; 
    uint8_t srp_I[16]; 

}; 

struct data { 
    long int foo; 
    short a; 
    uint8_t b; 
    struct packet p; 
    uint32_t bar; 
}; 

int main() { 
    struct packet p; 
    struct data d; 

    cout << "in: " << sizeof(input) << ", d: " << sizeof (d) << ", p: " << sizeof(p) << " d.p: " << sizeof(d.p) << endl; 
    memset(&p, 0, sizeof(p)); 
    memcpy(&p, input, sizeof(input)); 
    cout << (int) p.srp_I_len << endl; 
    cout << p.srp_I << endl; 
} 

$./foo 
in: 39, d: 72, p: 50 d.p: 50 
5 
ADMIN 
+0

Я получаю сообщение об ошибке: не могу связать упакованное поле «data.packet :: pkt_size» с «uint16_t &» и для игры, а также с 'uint32_t &'. – user2397418

+0

Можете ли вы показать свой прагма-код? Потому что он должен просто работать; кажется, что ваша структура пакета является частью другой структуры? –

+0

Я использую GCC, поэтому я сделал это: struct __attribute __ ((упакован)) AuthLogonChallenge {...}. Переменные в структуре - это uint8_t, uint16_t и uint32_t из stdint.h, я набирал их, чтобы сократить имя типа данных. – user2397418

0

Байт в структуре должен быть выровнен с 4 байтами.Вы должны ввести некоторые запасные переменные:

struct packet{ 
    uint8 cmd; 
    uint8 error; 
    uint16 pkt_size; 
    uint32 gamename; 
    uint8 version1; 
    uint8 version2; 
    uint8 version3; 
    uint8 spare1; 
    uint16 build; 
    uint8 spare2; 
    uint8 spare3; 
    uint32 platform; 
    uint32 os; 
    uint32 country; 
    uint32 timezone_bias; 
    uint8 ip[4]; 
    uint8 srp_I_len; 
    uint8 spare4[3]; 
    uint8 srp_I[16]; 
}; 
+0

Я попытался добавить запасные переменные, точно так же, как вы показали здесь, однако данные все еще хранятся некорректно. – user2397418

+0

Вы уверены, что реализовали эту последнюю версию? Я отредактировал его пару раз, потому что сделал некоторые ошибки – HAL9000

+0

Да, я поставил все 4 запасных части. – user2397418

Смежные вопросы