首页 文章

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

提问于
浏览
4

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

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

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

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

    • 它是可移植的 - 无论 intfloatdouble 的大小是什么 - 或者结构中字段之间的填充是什么 .

    • 它很容易调试(您只需查看数据并说明它是对还是错)

    • 发送/接收机器的字节顺序无关紧要 .

    另一方面,发送二进制数据很复杂,因为您需要担心数据字段的各个大小及其内部表示(字节顺序,如何在二进制中重新表示 double ,结构内数据字段的填充,不能通过指针等等) . 唯一的好处是二进制数据更紧凑 . 但这只会影响你是否有很多千字节和/或每秒发送大量数据包 .

  • 2

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

  • 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 ,我将它作为一种学习体验来实现 .

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

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

  • 2

    这取决于这必须是多么便携 .

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

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

相关问题