我试图将整个结构从客户端传递到服务器,反之亦然。让我们假设我的结构如下
struct temp {
int a;
char b;
}
我正在使用 sendto 并发送结构变量的地址,并使用 recvfrom 函数在另一侧接收它。但我无法获得接收端发送的原始数据。在sendto函数中,我将接收到的数据保存为struct temp。
类型的变量n = sendto(sock, &pkt, sizeof(struct temp), 0, &server, length);
n = recvfrom(sock, &pkt, sizeof(struct temp), 0, (struct sockaddr *)&from,&fromlen);
其中pkt是struct temp类型的变量。
虽然我收到了8字节的数据,但如果我尝试打印它只是显示垃圾值。对它有什么帮助吗?
注意: 不得使用第三方库。
EDIT1: 我对这个序列化概念真的很新..但是如果不进行序列化,我就不能通过套接字发送结构了吗?
EDIT2: 当我尝试使用 sendto 和 recvfrom发送字符串或整数变量 functions我在接收端正确接收数据。为什么不在结构的情况下?如果我不必使用序列化功能,那么我应该单独发送每个结构的成员吗?这实际上不是一个合适的解决方案,因为如果有'n'个成员,那么只需要添加'n'个代码行来发送或接收数据。
答案 0 :(得分:68)
这是一个非常糟糕的主意。二进制数据应始终以下列方式发送:
不要以二进制方式编写整个结构,而不是文件,而不是套接字。
始终分别编写每个字段,并以相同的方式阅读。
你需要有像
这样的功能unsigned char * serialize_int(unsigned char *buffer, int value)
{
/* Write big-endian int value into buffer; assumes 32-bit int and 8-bit char. */
buffer[0] = value >> 24;
buffer[1] = value >> 16;
buffer[2] = value >> 8;
buffer[3] = value;
return buffer + 4;
}
unsigned char * serialize_char(unsigned char *buffer, char value)
{
buffer[0] = value;
return buffer + 1;
}
unsigned char * serialize_temp(unsigned char *buffer, struct temp *value)
{
buffer = serialize_int(buffer, value->a);
buffer = serialize_char(buffer, value->b);
return buffer;
}
unsigned char * deserialize_int(unsigned char *buffer, int *value);
或等效的,当然有几种方法来设置缓冲区管理等等。然后,您需要执行序列化/反序列化整个结构的更高级函数。
这假设序列化是从缓冲区完成的,这意味着序列化不需要知道最终目标是文件还是套接字。它还意味着你需要支付一些内存开销,但出于性能原因它通常是一个很好的设计(你不想对socket的每个值执行write())。
完成上述操作后,您可以通过以下方式序列化和传输结构实例:
int send_temp(int socket, const struct sockaddr *dest, socklen_t dlen,
const struct temp *temp)
{
unsigned char buffer[32], *ptr;
ptr = serialize_temp(buffer, temp);
return sendto(socket, buffer, ptr - buffer, 0, dest, dlen) == ptr - buffer;
}
关于上述几点需要注意:
buffer
。sendto()
调用成功,则返回值为1,否则为0。答案 1 :(得分:10)
使用'pragma'pack选项确实解决了我的问题,但我不确定它是否有任何依赖?
#pragma pack(1) // this helps to pack the struct to 5-bytes
struct packet {
int i;
char j;
};
#pragma pack(0) // turn packing off
然后以下几行代码没有任何问题
n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length);
n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen);
答案 2 :(得分:6)
如果您不想自己编写序列化代码,请找到一个合适的序列化框架,然后使用它。
也许谷歌的protocol buffers是可能的?
答案 3 :(得分:6)
无需为short
和long
整数类型编写自己的序列化例程 - 使用htons()
/ htonl()
POSIX函数。
答案 4 :(得分:1)
序列化是一个好主意。您还可以使用Wireshark来监控流量并了解数据包中实际传递的内容。
答案 5 :(得分:0)
使用标签,长度和值很容易找到原始协议,而不是序列化和依赖第三方库。
Tag: 32 bit value identifying the field
Length: 32 bit value specifying the length in bytes of the field
Value: the field
根据需要连接。使用标签的枚举。并使用网络字节顺序...
易于编码,易于解码。
此外,如果您使用TCP,请记住它是流数据,因此如果您发送,例如3个数据包你不一定会收到3个数据包。它们可能会根据nodelay / nagel算法“合并”到一个流中,您可以将它们全部放在一个recv中......您需要使用RFC1006来分隔数据。
UDP更容易,每个发送的数据包都会收到一个不同的数据包,但安全性要低得多。
答案 6 :(得分:0)
如果要传输的数据格式非常简单,那么转换为ANSI字符串和从ANSI字符串转换是简单易用的。