C套接字发送/ recv缓冲区类型

时间:2013-03-01 23:47:27

标签: c++ c sockets unix

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

现在我想在一条消息中发送/ recv不同类型的数据 例如,在一条消息中,我想发送一个整数,一个字符串,一个double和一个float。 我该怎么做呢?更具体地说,我的消息'缓冲'应该是什么类型的?

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 ++和指针没有太多经验,所以这可能是一个noob问题。

如果有人能指导我朝着正确的方向前进,我真的很感激。 感谢

5 个答案:

答案 0 :(得分: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,然后write序列化为socket

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,我已将其作为学习体验实现。

答案 1 :(得分:2)

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

答案 2 :(得分:2)

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

  1. 它是可移植的 - 无论intfloatdouble的大小是什么 - 或者字段之间的填充在结构中的是什么。
  2. 调试很容易(您只需查看数据并说明它是对还是错)
  3. 发送/接收机器的字节顺序无关紧要。
  4. 另一方面,发送二进制数据很复杂,因为您需要担心数据字段的各个大小及其内部表示(字节顺序,如何在二进制中重置double,填充数据字段在结构内,不能传递指针等等)。唯一的好处是二进制数据更紧凑。但这只会影响你是否有很多千字节和/或每秒发送大量数据包。

答案 3 :(得分: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);

当您希望“移植”通过网络“移植”二进制数据时,您可以看到它开始变得特定于系统,这就是为什么人们要转换为文本,或者很可能使用已经编写的序列化库。

答案 4 :(得分:0)

这取决于必须具备的便携性。

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

您也可以将其转换为network byte order并将其作为二进制文件发送。