我有一个小型客户端服务器应用程序,我希望通过C而不是C++的TCP套接字发送整个结构.假设结构如下:
struct something{
int a;
char b[64];
float c;
}
Run Code Online (Sandbox Code Playgroud)
我发现很多帖子说我需要使用pragma pack或在发送和接收之前序列化数据.
我的问题是,使用JUST pragma pack还是仅仅序列化就足够了?或者我需要同时使用两者吗?
此外,由于序列化是处理器密集型过程,这会使您的性能急剧下降,那么在不使用外部库的情况下序列化结构的最佳方法是什么(我想要一个示例代码/算法)?
Amb*_*jak 15
您需要以下内容通过网络可移植地发送struct:
打包结构.对于gcc和兼容的编译器,请执行此操作__attribute__((packed)).
除了固定大小的无符号整数,满足这些要求的其他压缩结构或任何前者的数组之外,不要使用任何其他成员.有符号整数也可以,除非你的机器不使用二进制补码表示.
确定您的协议是使用整数的小端还是大端编码.在读取和写入这些整数时进行转换.
此外,不要使用压缩结构的成员指针,除了那些大小为1或其他嵌套打包结构的指针.看到这个答案.
下面是编码和解码的简单示例.它假定字节顺序转换函数hton8(),ntoh8(),hton32(),和ntoh32()是可用的(前两者都是一个无操作,但也有对一致性).
#include <stdint.h>
#include <inttypes.h>
#include <stdlib.h>
#include <stdio.h>
// get byte order conversion functions
#include "byteorder.h"
struct packet {
uint8_t x;
uint32_t y;
} __attribute__((packed));
static void decode_packet (uint8_t *recv_data, size_t recv_len)
{
// check size
if (recv_len < sizeof(struct packet)) {
fprintf(stderr, "received too little!");
return;
}
// make pointer
struct packet *recv_packet = (struct packet *)recv_data;
// fix byte order
uint8_t x = ntoh8(recv_packet->x);
uint32_t y = ntoh32(recv_packet->y);
printf("Decoded: x=%"PRIu8" y=%"PRIu32"\n", x, y);
}
int main (int argc, char *argv[])
{
// build packet
struct packet p;
p.x = hton8(17);
p.y = hton32(2924);
// send packet over link....
// on the other end, get some data (recv_data, recv_len) to decode:
uint8_t *recv_data = (uint8_t *)&p;
size_t recv_len = sizeof(p);
// now decode
decode_packet(recv_data, recv_len);
return 0;
}
Run Code Online (Sandbox Code Playgroud)
就字节顺序转换函数而言,系统的htons()/ ntohs()和htonl()/ ntohl()可以分别用于16位和32位整数转换为/从big-endian转换.但是,我不知道64位整数的任何标准函数,或者转换为/从小端转换.您可以使用我的字节顺序转换函数 ; 如果你这样做,你必须通过定义或告诉它你机器的字节顺序.BADVPN_LITTLE_ENDIANBADVPN_BIG_ENDIAN
就有符号整数而言,转换函数可以像我编写和链接的那样安全地实现(直接交换字节); 只需将unsigned更改为signed.
更新:如果你想要一个有效的二进制协议,但不喜欢摆弄字节,你可以尝试像协议缓冲区(C实现).这允许您在单独的文件中描述消息的格式,并生成用于编码和解码您指定格式的消息的源代码.我自己也实现了类似的东西,但大大简化了; 请参阅我的BProto生成器和一些示例(查看.bproto文件,以及addr.h作为用法示例).
在通过 TCP 连接发送任何数据之前,请制定协议规范。它不必是充满技术术语的多页文档。但它必须指定谁在何时传输什么内容,并且必须在字节级别指定所有消息。它应该指定如何建立消息的结尾、是否有任何超时以及由谁施加超时,等等。
如果没有规范,很容易提出根本无法回答的问题。如果出了问题,到底是哪一端出了问题呢?有了规范,不符合规范的那一端就有问题。(如果两端都遵循规范但仍然不起作用,则说明规范有问题。)
一旦有了规范,就可以更容易地回答有关如何设计一端或另一端的问题。
我还强烈建议不要围绕硬件的具体情况设计网络协议。至少,并非没有经过证实的性能问题。