我用C ++编写了一个服务器应用程序,它从客户端应用程序(我自己没有编写)接收数据包,并将数据打印到控制台。问题是,当我尝试一次接收和存储整个数据包主体时,数据存储不正确,但是当使用recv()的多个调用接收和存储数据包主体时,它确实存储正确。
关于字节序,客户端和服务器都运行在一个小端机器上,客户端以小端发送数据,服务器读取它而不需要转换。
这是客户端应用程序发送到服务器应用程序的数据包:
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(),它会按预期接收并存储数据。当我只调用两次recv()时,我完全不知道为什么数据存储不正确。拜托,有人,赐教。谢谢。
PS:很抱歉这个丑陋的怪物。
答案 0 :(得分:1)
将结构声明为打包结构以避免对齐问题;
在Windows上使用#pragma pack (1)
(请参阅msdn)
在gcc上使用__attribute__((packed))
删除对齐问题。实际上gcc将支持windows风格的pragma以实现兼容性。看看:
http://gcc.gnu.org/onlinedocs/gcc/Structure_002dPacking-Pragmas.html
修改
下面的示例代码显示了另一个未触及结构中的ONE压缩结构:
在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
答案 1 :(得分: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];
};