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

时间:2022-08-26 18:44:30

I am trying to pass whole structure from client to server or vice-versa. Let us assume my structure as follows

我正在尝试将整个结构从客户机传递到服务器,或者相反。让我们假设我的结构如下

struct temp {
  int a;
  char b;
}

I am using sendto and sending the address of the structure variable and receiving it on the other side using the recvfrom function. But I am not able to get the original data sent on the receiving end. In sendto function I am saving the received data into variable of type struct temp.

我正在使用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);

Where pkt is the variable of type struct temp.

其中pkt是struct temp类型的变量。

Eventhough I am receiving 8bytes of data but if I try to print it is simply showing garbage values. Any help for a fix on it ?

虽然我接收了8字节的数据,但如果我尝试打印,它只是显示垃圾值。有任何补救措施吗?

NOTE: No third party Libraries have to be used.

注意:不需要使用第三方库。

EDIT1: I am really new to this serialization concept .. But without doing serialization cant I send a structure via sockets ?

我对这个序列化概念很陌生。但是如果不进行序列化,我就不能通过套接字发送结构吗?

EDIT2: When I try to send a string or an integer variable using the sendto and recvfrom functions I am receiving the data properly at receiver end. Why not in the case of a structure? If I don't have to use serializing function then should I send each and every member of the structure individually? This really is not a suitable solution since if there are 'n' number of members then there are 'n' number of lines of code added just to send or receive data.

当我尝试使用sendto和recvfrom函数发送一个字符串或一个整数变量时,我正在接收端正确地接收数据。为什么不考虑结构呢?如果我不需要使用序列化函数,那么我应该将结构的每个成员分别发送吗?这确实不是一个合适的解决方案,因为如果有n个成员,那么仅仅为了发送或接收数据而添加了n行代码。

7 个解决方案

#1


62  

This is a very bad idea. Binary data should always be sent in a way that:

这是个坏主意。二进制数据应始终以以下方式发送:

Don't ever write a whole struct in a binary way, not to a file, not to a socket.

不要用二进制的方式编写整个结构体,不要写文件,不要写套接字。

Always write each field separately, and read them the same way.

总是分开写每个字段,并以同样的方式读取它们。

You need to have functions like

你需要有像这样的函数

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

Or the equivalent, there are of course several ways to set this up with regards to buffer management and so on. Then you need to do the higher-level functions that serialize/deserialize entire structs.

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

This assumes serializing is done to/from buffers, which means the serialization doesn't need to know if the final destination is a file or a socket. It also means you pay some memory overhead, but it's generally a good design for performance reasons (you don't want to do a write() of each value to the socket).

这假定序列化是从缓冲区执行的,这意味着序列化不需要知道最终目标是文件还是套接字。这也意味着您需要支付一些内存开销,但是由于性能原因(您不希望对每个值对套接字执行写操作),这通常是一个很好的设计。

Once you have the above, here's how you could serialize and transmit a structure instance:

一旦有了上面的内容,以下是如何序列化和传输结构实例的方法:

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

A few points to note about the above:

关于以上几点需要注意:

  • The struct to send is first serialized, field by field, into buffer.
  • 要发送的结构体首先被序列化,逐个字段地发送到缓冲区。
  • The serialization routine returns a pointer to the next free byte in the buffer, which we use to compute how many bytes it serialized to
  • 序列化例程返回一个指向缓冲区中下一个空闲字节的指针,我们使用它来计算它序列化到的字节数。
  • Obviously my example serialization routines don't protect against buffer overflow.
  • 显然,我的示例序列化例程不能防止缓冲区溢出。
  • Return value is 1 if the sendto() call succeeded, else it will be 0.
  • 如果sendto()调用成功,则返回值为1,否则返回值为0。

#2


7  

Using the 'pragma' pack option did solved my problem but I am not sure if it has any dependencies ??

使用“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

Then the following lines of code worked out fine without any problem

接下来的几行代码很好地完成了,没有任何问题

n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length);

n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen);

#3


6  

If you don't want to write the serialisation code yourself, find a proper serialisation framework, and use that.

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

Maybe Google's protocol buffers would be possible?

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

#4


4  

There is no need to write own serialisation routines for short and long integer types - use htons()/htonl() POSIX functions.

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

#5


1  

Serialization is a good idea. You can also use "wireshark" to monitor the traffic and understand what is actually passed in the packets.

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

#6


0  

Instead of serialising and depending on 3rd party libraries its easy to come up with a primitive protocol using tag, length and value.

与序列化和依赖第三方库相比,它更容易使用标记、长度和值生成原始协议。

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

Concatenate as required. Use enums for the tags. And use network byte order...

根据需要连接。为标签使用枚举。使用网络字节顺序…

Easy to encode, easy to decode.

易于编码,易于解码。

Also if you use TCP remember it is a stream of data so if you send e.g. 3 packets you will not necessarily receive 3 packets. They maybe be "merged" into a stream depending on nodelay/nagel algorithm amongst other things and you may get them all in one recv... You need to delimit the data for example using RFC1006.

另外,如果您使用TCP,请记住它是一个数据流,所以如果您发送3个包,您不一定会收到3个包。它们可能被“合并”到一个流中,这取决于nodelay/nagel算法以及其他一些东西,你可能会在一个recv中得到它们……您需要对数据进行分隔,例如使用RFC1006。

UDP is easier, you'll receive a distinct packet for each packet sent, but its a lot less secure.

UDP更简单,你会收到一个不同的数据包发送,但它的安全性要低得多。

#7


0  

If the format of the data you want to transfer is very simple then converting to and from an ANSI string is simple and portable.

如果您想要传输的数据的格式非常简单,那么从ANSI字符串转换为或转换为ANSI字符串是简单且可移植的。

#1


62  

This is a very bad idea. Binary data should always be sent in a way that:

这是个坏主意。二进制数据应始终以以下方式发送:

Don't ever write a whole struct in a binary way, not to a file, not to a socket.

不要用二进制的方式编写整个结构体,不要写文件,不要写套接字。

Always write each field separately, and read them the same way.

总是分开写每个字段,并以同样的方式读取它们。

You need to have functions like

你需要有像这样的函数

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

Or the equivalent, there are of course several ways to set this up with regards to buffer management and so on. Then you need to do the higher-level functions that serialize/deserialize entire structs.

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

This assumes serializing is done to/from buffers, which means the serialization doesn't need to know if the final destination is a file or a socket. It also means you pay some memory overhead, but it's generally a good design for performance reasons (you don't want to do a write() of each value to the socket).

这假定序列化是从缓冲区执行的,这意味着序列化不需要知道最终目标是文件还是套接字。这也意味着您需要支付一些内存开销,但是由于性能原因(您不希望对每个值对套接字执行写操作),这通常是一个很好的设计。

Once you have the above, here's how you could serialize and transmit a structure instance:

一旦有了上面的内容,以下是如何序列化和传输结构实例的方法:

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

A few points to note about the above:

关于以上几点需要注意:

  • The struct to send is first serialized, field by field, into buffer.
  • 要发送的结构体首先被序列化,逐个字段地发送到缓冲区。
  • The serialization routine returns a pointer to the next free byte in the buffer, which we use to compute how many bytes it serialized to
  • 序列化例程返回一个指向缓冲区中下一个空闲字节的指针,我们使用它来计算它序列化到的字节数。
  • Obviously my example serialization routines don't protect against buffer overflow.
  • 显然,我的示例序列化例程不能防止缓冲区溢出。
  • Return value is 1 if the sendto() call succeeded, else it will be 0.
  • 如果sendto()调用成功,则返回值为1,否则返回值为0。

#2


7  

Using the 'pragma' pack option did solved my problem but I am not sure if it has any dependencies ??

使用“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

Then the following lines of code worked out fine without any problem

接下来的几行代码很好地完成了,没有任何问题

n = sendto(sock,&pkt,sizeof(struct packet),0,&server,length);

n = recvfrom(sock, &pkt, sizeof(struct packet), 0, (struct sockaddr *)&from, &fromlen);

#3


6  

If you don't want to write the serialisation code yourself, find a proper serialisation framework, and use that.

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

Maybe Google's protocol buffers would be possible?

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

#4


4  

There is no need to write own serialisation routines for short and long integer types - use htons()/htonl() POSIX functions.

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

#5


1  

Serialization is a good idea. You can also use "wireshark" to monitor the traffic and understand what is actually passed in the packets.

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

#6


0  

Instead of serialising and depending on 3rd party libraries its easy to come up with a primitive protocol using tag, length and value.

与序列化和依赖第三方库相比,它更容易使用标记、长度和值生成原始协议。

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

Concatenate as required. Use enums for the tags. And use network byte order...

根据需要连接。为标签使用枚举。使用网络字节顺序…

Easy to encode, easy to decode.

易于编码,易于解码。

Also if you use TCP remember it is a stream of data so if you send e.g. 3 packets you will not necessarily receive 3 packets. They maybe be "merged" into a stream depending on nodelay/nagel algorithm amongst other things and you may get them all in one recv... You need to delimit the data for example using RFC1006.

另外,如果您使用TCP,请记住它是一个数据流,所以如果您发送3个包,您不一定会收到3个包。它们可能被“合并”到一个流中,这取决于nodelay/nagel算法以及其他一些东西,你可能会在一个recv中得到它们……您需要对数据进行分隔,例如使用RFC1006。

UDP is easier, you'll receive a distinct packet for each packet sent, but its a lot less secure.

UDP更简单,你会收到一个不同的数据包发送,但它的安全性要低得多。

#7


0  

If the format of the data you want to transfer is very simple then converting to and from an ANSI string is simple and portable.

如果您想要传输的数据的格式非常简单,那么从ANSI字符串转换为或转换为ANSI字符串是简单且可移植的。