我用C编写了一个服务器应用程序,它从客户机应用程序(我自己没有编写)接收数据包,并将数据打印到控制台。问题是,当我试图一次接收并存储整个包体时,数据存储不正确,但是当使用recv()的多次调用接收并存储包体时,它确实正确存储。
关于endianness,客户端和服务器都运行在一个小端机器上,客户端作为小端发送数据,服务器读取数据而不需要转换。
这是客户端应用程序发送给服务器应用程序的数据包:
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:很抱歉发布了丑陋的怪物帖子。
结构中的字节必须与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];
};
声明结构为填充结构,以避免对齐问题;
在windows上使用#pragma pack(1)
(请参阅msdn)
在gcc使用__attribute__((打包))
以消除对齐问题。实际上,为了兼容性,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
我需要你的帮助。有一个Vue应用程序,我使用vuex商店和Vue路由器。问题是,我从API调用中获取存储数据,如果我从其他地方导航到页面,用户数据已经存储在存储中,并且在组件中,我可以使用getter接收到的数据。但是,如果我在create或mount方法中重新加载页面,则getter不包含任何数据。但如果在Vuex开发工具中查看,我可以看到fetchedd数据。如何修复此行为?
本文向大家介绍C语言二进制思想以及数据的存储,包括了C语言二进制思想以及数据的存储的使用技巧和注意事项,需要的朋友参考一下 我们平时使用的数字都是由 0~9 共十个数字组成的,例如 1、9、10、297、952 等,一个数字最多能表示九,如果要表示十、 十一、二十九、一百等,就需要多个数字组合起来。 例如表示 5+8 的结果,一个数字不够,只能”进位“,用 13 来表示;这时”进一位“相当于十,”
本文向大家介绍c#语言连接,包括了c#语言连接的使用技巧和注意事项,需要的朋友参考一下 示例 联接用于通过公共键合并保存数据的不同列表或表。 像在SQL中一样,LINQ支持以下类型的联接: 内联接,左联接,右联接,交叉联接和完全外联接。 以下示例中使用了以下两个列表: (内部联接 左外连接 右外连接 交叉连接 完全外部加入 实际例子 上面的示例具有简单的数据结构,因此您可以专注于从技术上理解不同的
本文向大家介绍C语言 数据结构堆排序顺序存储(升序),包括了C语言 数据结构堆排序顺序存储(升序)的使用技巧和注意事项,需要的朋友参考一下 堆排序顺序存储(升序) 一: 完全二叉树的概念:前h-1层为满二叉树,最后一层连续缺失右结点! 二:首先堆是一棵全完二叉树: a:构建一个堆分为两步:⑴创建一棵完全二叉树 ⑵调整为一个堆 (标注:大根堆为升序,小根堆为降序) b:算法描述:①创
1. 引言 其实我们不知道的是,早期的计算机是没有内存的,但是如今我们去买电脑时,都会十分关心电脑内存的各种参数,因此可以看出内存对于电脑性能的重要性。那么为什么需要内存呢?换句话说,内存与计算机以及程序之间的关系又是什么呢?本章将会以内存为中心,探讨许多与内存相关的概念和话题,这些概念是学好后续C语言知识不可缺少的基础,因此希望读者认真对待本章节的内容。 2. 计算机程序运行的目的 2.1 什么
问题内容: 我正在使用和用于应用程序。 问题是: 如何以 可读格式或格式存储数据 当用户在文本框中输入数据并单击“提交”时,那时我们将获得不同格式的数据。我们需要做的是我们以可读格式转换和存储。 问题答案: 选择utf8字符集和排序规则。 显然,该字段(要将印度语文本存储到的字段)的排序规则应为。 要更改表字段,请运行 连接到数据库后,首先运行以下语句 例如: 检索数据 在浏览器上打印任何unic