4

我正在使用 unix 套接字,当我的缓冲区为 char 类型(即发送和接收字符串)时,我可以发送()和 ​​recv()数据。我使用了 Beej 的套接字指南,使用的示例用于发送/接收字符串。

现在我想在一条消息中发送/接收不同类型的数据。
例如,假设在一条消息中我想发送一个整数、一个字符串、一个双精度数和一个浮点数。我该怎么做呢?更具体地说,我的消息“缓冲区”应该是什么类型的?

send 和 recv 的原型:

int recv (int socket, void *buffer, size_t size, int flags)
int send (int socket, void *buffer, size_t size, int flags)

我对 C/C++ 和指针没有太多经验,所以这可能是一个菜鸟问题。

如果有人能引导我朝着正确的方向前进,我将不胜感激。谢谢

4

5 回答 5

2

最好的方法是将要传递的信息封装在一个结构中。然后传递结构的地址和长度。请注意,buffervoid*允许您传递包含您的信息的结构的地址。

这是struct我用于安全副本之的工具的一小部分,

struct message
{
     size_t total_len;
     size_t filename_len;
     char *filename;
     size_t text_len;
     char *text;
     char *iv;
     char *salt;
     unsigned char *hmac;
};

然后在你的send_message()序列化中message再到writesocket

bool send_message(const struct message *msg, const char *ip, const char *port)
{
    ...
   connect(sockfd, (struct sockaddr *)&server_addr, sizeof(server_addr));
   // Serialize to file
   serialize(msg, "temp");

   // Transfer the file over the network
   fp = fopen("temp", "rb");
   while((n = fread(buffer, 1, MAX_BUF_LEN, fp)) != 0)
   {
       write(sockfd, buffer, n);
   }

   fclose(fp);
   remove("temp");

   close(sockfd);
}

正如@vitaut 所建议的,你可以使用一个库来serialize,我已经将它作为一种学习体验来实现。

于 2013-03-01T23:49:17.353 回答
2

您需要在发送方序列化对象并在接收方反序列化。C++ 中有许多用于此的库,例如Boost Serialization,或者如果您想重新发明轮子,您可以随时自己动手。

于 2013-03-01T23:52:04.247 回答
2

除非您打算发送大量数据(数千字节)并且经常(每秒几个数据包),否则我建议您将数据转换为字符串(也称为“序列化数据”)并以这种方式传递。它有几个好处:

  1. 它是可移植的——无论大小intfloat或是double什么——或者结构中字段之间的填充是什么,它都可以工作。
  2. 很容易调试(你可以只看数据并说出它是对还是错)
  3. 发送/接收机器的字节顺序无关紧要。

另一方面,发送二进制数据很复杂,因为您需要担心数据字段的各个大小及其内部表示(字节顺序、double二进制如何表示 a、结构中数据字段的填充、不能传递指针等)。唯一的好处是二进制数据更紧凑一些。但这仅在您有很多千字节和/或每秒发送大量数据包时才重要。

于 2013-03-01T23:54:36.923 回答
2

好吧,要回答所问的问题(“我如何发送结构?”),您只需发送一个指向该结构的指针。

struct foo
{
  int a;
  int b;
};

struct foo s;
s.a = 10;
s.b = 20;
send(socket,&s,sizeof(s),0);

它真的很容易。现在,另一边必须相同(即,系统 A 上内存中布局的结构必须与系统 B 上布局的结构相匹配)。更好的方法是使用更具体的类型和一些函数来正确排序数据:

#ifndef __GNUC__
#  define __attribute__(x)
#endif

#ifdef __SunOS
#  pragma pack(1)
#endif

struct foo
{
  uint32_t a;
  uint32_t b;
} __attribute__((packed));

#ifdef __SunOS
#  pragma pack()
#endif

struct foo s

/* to send */

s.a = htonl(10);
s.b = htonl(20);
send(socket,&s,sizeof(s),0);

/* to receive */
recv(socket,&s,sizeof(s),0);
s.a = ntohl(s.a);
s.b = ntohl(s.b);

当您想通过网络“可移植地”发送二进制数据时,您可以看到它开始相当快地获得系统特定,这就是人们说转换为文本或很可能使用已经编写的序列化库的原因。

于 2013-03-02T09:30:32.427 回答
0

这取决于它的便携性。

最简单的方法是将所有内容转换为用逗号或分号分隔的 ASCII,然后在接收端再次解析。

您还可以将其转换为网络字节顺序并以二进制形式发送。

于 2013-03-01T23:52:45.653 回答