每个变量都存在于您计算机的内存中。内存以字节为单位。
当您编写 C++ 代码时,您可以直接读取这些字节。对于一个结构,其所有成员的内存都在一个连续的块中(尽管每个成员之间可能存在间隙)。
所以,如果我声明:
struct foo {
char x;
char y;
short z;
int q;
};
然后,当我创建 struct foo 时,我在内存中得到以下布局(在大多数系统上总共 8 个字节):
xyzzqqqq
第一个字节是x,第二个字节是y,第三个和第四个一起是z,最后四个是q。
所以,对象已经“序列化”了——你有一堆代表它的字节。这就是您需要通过网络发送的全部内容:代表数据结构的信息。
您编写自己的序列化程序的原因是您可能想要更改对象的读取或写入方式(例如,如果我向struct foo 添加一个字段会怎样? ),因为您需要在内存布局不同的机器之间进行通信(z 的哪个字节代表数字的“最重要”部分?),或者因为您只想序列化结构的一部分(如果我们成员之间有一些空白空间吗?)。
但是,从根本上说,您发送“char 数据”的原因是因为您计算机中的所有内容都可以以这种方式表示。我不会讨论图灵关于符号编码的证明,但任何知识都可以编码为一系列 1 和 0,这是一个数学上的确定。
更具体地说,将数据放入数据包的“char data”字段的方式是通过memcpying 从数据当前放入缓冲区的位置。所以如果我有一个char* target,我可以这样写一个struct foo x:
memcpy(target, &x, sizeof(struct foo));
或者我可以通过编写每个字段来更仔细地做到这一点:
memcpy(target, &x.x, 1);
memcpy(target+1, &x.y, 1);
memcpy(target+2, &x.z, sizeof(short));
memcpy(target+4, &x.q, sizeof(int));
& 是地址运算符,如果您还不知道的话。所以我从每个成员的地址开始写入target 内的某个偏移量,并写入等于成员变量表示长度的字节数。
对您最后一个问题的公认答案指出,这是一种过度简化:当您通过网络发送多字节整数时,您必须担心 endianness(字节顺序)。所以你实际做的是:
memcpy(target, &x.x, 1);
memcpy(target+1, &x.y, 1);
*((short*)(target+2)) = htons(x.z);
*((int*)(target+4)) = htonl(x.q);
这将根据需要处理反转字节以将主机字节顺序转换为网络字节顺序。显然,一字节长的值是免疫的。