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

发布于 2024-08-07 11:40:52 字数 913 浏览 14 评论 0原文

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

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:当我尝试发送字符串或整数变量时使用 sendtorecvfrom 函数我在接收端正确接收数据。为什么不在结构体的情况下呢?如果我不必使用序列化函数,那么我应该单独发送结构的每个成员吗?这确实不是一个合适的解决方案,因为如果有“n”个成员,那么就会添加“n”行代码来发送或接收数据。

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.

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.

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 ?

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.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(7

暮倦 2024-08-14 11:40:52

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

永远不要以二进制方式写入整个结构,不要写入文件,不要写入套接字。

始终单独写入每个字段,并以相同的方式读取它们。

您需要具有类似的功能

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

或等效功能,当然有多种方法可以在缓冲区管理等方面进行设置。然后,您需要执行序列化/反序列化整个结构的高级函数。

这假设序列化是从缓冲区完成的,这意味着序列化不需要知道最终目的地是文件还是套接字。这也意味着您需要付出一些内存开销,但出于性能原因,这通常是一个很好的设计(您不想对套接字的每个值执行 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;
}

关于上述内容需要注意的几点:

  • 要发送的结构体首先逐个字段序列化到缓冲区中。
  • 序列化例程返回一个指向缓冲区中下一个空闲字节的指针,我们用它来计算它序列化了多少字节。
  • 显然,我的示例序列化例程不能防止缓冲区溢出。
  • 如果 sendto() 调用成功,返回值为 1,否则返回 0。

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.
眼泪淡了忧伤 2024-08-14 11:40:52

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

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

#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);
泪冰清 2024-08-14 11:40:52

无需为 shortlong 整数类型编写自己的序列化例程 - 使用 htons()/htonl() POSIX 函数。

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

裸钻 2024-08-14 11:40:52

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

也许 Google 的协议缓冲区可行?

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?

千纸鹤带着心事 2024-08-14 11:40:52

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

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

他不在意 2024-08-14 11:40:52

无需序列化和依赖第三方库,您可以轻松地使用标签、长度和值提出原始协议。

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 算法等“合并”到一个流中,并且您可以将它们全部包含在一次接收中...您需要使用 RFC1006 来分隔数据。

UDP 更容易,对于发送的每个数据包,您都会收到一个不同的数据包,但它的安全性要低得多。

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.

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

傲世九天 2024-08-14 11:40:52

如果要传输的数据格式非常简单,那么与 ANSI 字符串之间的转换既简单又可移植。

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.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文