Reputation: 3
I have written a server application in C++ that receives a packet of data from a client application (which I have not written myself) and prints the data to the console. The issue is, when I try to receive and store the entire packet body at once, the data is stored incorrectly, however when the packet body is received and stored using multiple callings of recv(), it does store correctly.
Regarding endianness, the client and server are both running on a little endian machine, the client sends data out as little endian, and the server reads it without need for conversion.
This is the packet that the client application sends to the server application:
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
Here is a structured view of the packet:
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
These are the expected results to be printed out by the server application:
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
Here is the code which I am having trouble with:
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);
The result:
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
If I change the code like so:
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
The result:
... 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
And if I make one last adjustment to the code, like so:
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
The result:
... 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
By calling recv() multiple times, it received and stores the data exactly as expected. I have absolutely no idea why the data is stored incorrectly when only calling recv() twice. Please, somebody, enlighten me. Thank you.
PS: Sorry for the monster post of ugliness.
Upvotes: 0
Views: 178
Reputation: 22382
Declare the structure as a packed structure to avoid alignment issues;
On windows use #pragma pack (1)
(see msdn)
On gcc use __attribute__((packed))
to remove alignment problems. Actually gcc will support the windows style pragmas for compatibility. Have a look at:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
EDIT
So example code below shows ONE packed structure inside another untouched structure:
When compiled on x86_64-bit platform:
#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
Upvotes: 1
Reputation: 3751
The bytes in the structure must be aligned to 4 bytes. You have to introduce some spare variables:
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];
};
Upvotes: 0