【问题标题】:Issue receiving and storing data in C++在 C++ 中接收和存储数据的问题
【发布时间】:2013-05-13 17:14:15
【问题描述】:

我用 C++ 编写了一个服务器应用程序,它从客户端应用程序(我自己没有编写)接收数据包并将数据打印到控制台。问题是,当我尝试一次接收和存储整个数据包主体时,数据存储不正确,但是当使用多个recv()调用接收和存储数据包主体时,它确实存储正确。

关于字节序,客户端和服务器都运行在一个little endian机器上,客户端以little endian的形式发送数据,服务器无需转换即可读取。

这是客户端应用程序发送给服务器应用程序的数据包:

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:对于丑陋的怪物帖子感到抱歉。

【问题讨论】:

  • 出于优化原因,C 编译器在您的struct packet 中留下了空白。但是,通过网络发送的数据包中没有任何间隙。如果我这样做,我会将数据包recv 放入临时字节缓冲区,然后手动从缓冲区读取每个字段并将其写入我自己的结构,避免所有结构对齐问题。您的代码还假定与远程主机相同。

标签: c++ networking


【解决方案1】:

将结构声明为打包结构以避免对齐问题;

在 Windows 上使用#pragma pack (1)(参见msdn

在 gcc 上使用 __attribute__((packed))

消除对齐问题。实际上 gcc 将支持 windows 风格的编译指示以实现兼容性。看看:

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

编辑

所以下面的示例代码显示了另一个未触及结构中的一个打包结构:

在 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

【讨论】:

  • 我收到“错误:无法将打包字段 'data.packet::pkt_size' 绑定到 'uint16_t&'”和游戏名称以及 'uint32_t&'。
  • 你能显示你的编译代码吗?因为它应该可以工作;看来您的数据包结构是另一个结构的一部分?
  • 我正在使用 GCC,所以我这样做了:struct __attribute__((packed)) AuthLogonChallenge{ ... }。 struct 中的变量是 stdint.h 中的 uint8_t、uint16_t 和 uint32_t,我对它们进行了 typedef 以缩短数据类型名称。
  • 好的,我将发布一些代码......我知道它可以工作。我假设您使用的是 c++; (因为那是您标记的内容,但它也适用于 C 语言)希望它能给您提供足够的线索来使您的情况正常工作......待机
  • 我使用了#pragma pack(1),它有效!非常感谢。我仍然不明白为什么会出错,以及这行代码有什么帮助......
【解决方案2】:

结构中的字节必须对齐到 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];
};

【讨论】:

  • 我已经尝试添加备用变量,正如您在此处显示的那样,但是数据仍然存储不正确。
  • 你确定你实现了这个最新版本吗?我编辑了几次,因为我犯了一些错误
  • 是的,我把所有 4 个备件都放进去了。
猜你喜欢
  • 2011-05-30
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2012-06-17
  • 1970-01-01
相关资源
最近更新 更多