【问题标题】:C sockets send/recv buffer typeC 套接字发送/接收缓冲区类型
【发布时间】:2013-02-16 14:37:16
【问题描述】:

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

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

发送和接收的原型:

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

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

如果有人能指引我正确的方向,我将不胜感激。 谢谢

【问题讨论】:

    标签: c++ c sockets unix


    【解决方案1】:

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

    1. 它是可移植的 - 无论 intfloatdouble 的大小是什么 - 或者结构中字段之间的填充是什么都可以。
    2. 调试简单(只要看数据就知道是对是错)
    3. 发送/接收机器的字节顺序无关紧要。

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

    【讨论】:

    • 我同意这个建议,但在将doublefloat 序列化为字符串时仍需注意避免舍入问题。
    • 是的,处理浮点数总是有点混乱——它们只是不太友好[至少如果你想获得完整的精度则不是]。
    【解决方案2】:

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

    【讨论】:

    【解决方案3】:

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

    这是我用于安全复制like工具的struct的小sn-p,

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

    【讨论】:

      【解决方案4】:

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

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

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

      【讨论】:

        【解决方案5】:

        这取决于它的便携性。

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

        您也可以将其转换为network byte order 并以二进制形式发送。

        【讨论】:

          猜你喜欢
          • 2018-11-08
          • 1970-01-01
          • 1970-01-01
          • 2014-05-10
          • 1970-01-01
          • 1970-01-01
          • 2011-11-15
          • 2010-11-09
          • 2021-03-15
          相关资源
          最近更新 更多