通过C中的套接字传递一个结构

我试图将整个结构从客户端传递到服务器,反之亦然。 让我们假设我的结构如下

struct temp { int a; char b; } 

我正在使用sendto并发送结构variables的地址,并使用recvfrom函数在另一端接收它。 但是我无法获得在接收端发送的原始数据。 在sendto函数中,我将接收到的数据保存到struct temptypes的variables中。

 n = sendto(sock, &pkt, sizeof(struct temp), 0, &server, length); n = recvfrom(sock, &pkt, sizeof(struct temp), 0, (struct sockaddr *)&from,&fromlen); 

其中pkt是struct temptypes的variables。

尽pipe我正在接收8bytes的数据,但如果我尝试打印它只是显示垃圾值。 任何帮助解决它?

注: 不得使用第三方库。

编辑1:我对这个序列化的概念是新的。但是没有做序列化我不能通过套接字发送结构?

编辑2:当我尝试发送一个string或一个整数variables使用sendtorecvfrom函数我正在接收端的数据正确接收。 为什么不在一个结构的情况下? 如果我不必使用序列化函数,那么我应该单独发送结构的每个成员? 这实际上不是一个合适的解决scheme,因为如果有'n'个成员,那么为了发送或接收数据就会添加“n”行代码。

这是一个非常糟糕的主意。 二进制数据应始终以以下方式发送:

  • 处理不同的字节顺序
  • 处理不同的填充
  • 处理内在types字节大小的差异

不要以二进制方式写一个完整的结构体,而不是写入一个文件,而不是一个套接字。

总是分开写每个字段,并以相同的方式阅读。

你需要有像这样的function

 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); 

或者等价的,当然有几种方法来设置缓冲区pipe理等等。 然后,您需要执行序列化/反序列化整个结构的更高级别的函数。

这假设序列化是从缓冲区完成的,这意味着序列化不需要知道最终目的地是文件还是套接字。 这也意味着你花费了一些内存开销,但是由于性能方面的原因,这通常是一个很好的devise(你不希望每个值都写入socket)。

一旦你有了上面的内容,你可以如何序列化和传输一个结构实例:

 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。

使用'pragma'包选项解决了我的问题,但我不确定它是否有任何依赖关系?

 #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); 

如果你不想自己编写序列化代码,找一个合适的序列化框架,并使用它。

也许Google的协议缓冲区是可能的?

不需要为short整型和long整型编写自己的序列化例程 – 使用htons() / htonl() POSIX函数。

如果你需要可移植性(读取其他平台/体系结构上的数据),那么你必须单独序列化每个成员,由于字节数和结构填充。

这里是一个使用Binn的例子:

  binn *obj; // create a new object obj = binn_object(); // add values to it binn_object_set_int32(obj, "id", 123); binn_object_set_str(obj, "name", "Samsung Galaxy Charger"); binn_object_set_double(obj, "price", 12.50); binn_object_set_blob(obj, "picture", picptr, piclen); // send over the network send(sock, binn_ptr(obj), binn_size(obj)); // release the buffer binn_free(obj); 

它只是2个文件(binn.c和binn.h),所以它可以用项目编译而不是用作共享库。

对于TCP套接字,使用消息成帧(也称为长度前缀成帧)也是一个好主意。

序列化是一个好主意。 您也可以使用“wireshark”来监视stream量,并了解数据包中实际传递的内容。

取代序列化,并依赖于第三方库,它很容易拿出一个原始的协议,使用标签,长度和价值。

 Tag: 32 bit value identifying the field Length: 32 bit value specifying the length in bytes of the field Value: the field 

按要求连接。 使用枚举的标签。 并使用networking字节顺序…

易于编码,易于解码。

另外如果你使用TCP记住它是一个数据stream ,所以如果你发送例如3个数据包,你不一定会收到3个数据包。 根据nodelay / nagelalgorithm,它们可能被“合并”成一个stream,而且你可以在一个recv中得到它们。你需要使用RFC1006来分隔数据。

UDP比较容易,你会收到一个不同的数据包发送每个数据包,但它的安全性很差。

如果要传输的数据格式非常简单,那么与ANSIstring进行转换就非常简单且便于携带。