C ++如何通过套接字发送结构?

时间:2011-12-14 23:27:52

标签: c++ sockets serialization struct

说我有一个结构:

struct person
{
    char name[10];
    int age;
};

struct car
{
    int locationX;
    int locationY;
};

struct company
{
    vector<person> employees;
    vector<car> cars;
};

例如,我希望send/recv使用套接字(UDP)company。所以,发送和recv一次。

我该怎么做?你能给我一些代码吗?如何发送所有内容并阅读所有内容。

谢谢!

5 个答案:

答案 0 :(得分:17)

你的问题的措词表明你正在寻找的是:

company foo;
send(sockfd, &foo, sizeof(foo), 0); // DO NOT do this

这基本上会将company结构的所有内存转储到套接字中。在这种情况下,将无法工作。即使它有点工作,这也是一个非常糟糕的主意。它不起作用的原因是vector不直接包含数据。他们指着它。这意味着当您将包含向量的结构转储到套接字中时,您将转储指向内存的指针,但不会转向指向的内容。这将导致接收方(最好)崩溃。

对于单个personcar对象,它会起作用。它们不包含指针,因此它们的内存包含所有相关值'

在发送方:

person joe = { "Joe", 35 };
send(sockfd, &joe, sizeof(joe), 0); // may work, but is a bad idea, see below

在接收方:

person joe;
recv(sockfd, &joe, sizeof(joe), 0);

但是,这仍然是一个坏主意。它依赖于发送方和接收方,它们的结构具有完全相同的存储器布局。出于各种原因,这可能不是真的。有些包括一个在PowerPC芯片上,另一个在Intel x86芯片上。或者一个在使用Visual Studio编译的Windows机器上,另一个在使用gcc编译的Linux机器上。或许有人调整了一些编译器标志,导致默认结构布局不同。有很多原因。

真的,你应该像所有人建议的那样使用序列化框架。我建议Google protocol buffers或其他人已经关联的Boost serialization framework。但还有很多其他人。

应该提到的另一个序列化框架,因为它非常快(几乎与直接将结构的内存映像转储到套接字中一样快)是Cap'n Proto

答案 1 :(得分:8)

查看Google协议缓冲区http://code.google.com/apis/protocolbuffers/ 作为Boost序列化的精简替代品。

答案 2 :(得分:7)

如果要制作其中许多内容,您可能需要查看Thrift。它将自动生成所有必要的代码,以便为您序列化所有结构,因此您无需手动执行此操作。

他们支持字符串非常实用。

哦!它是免费的。 8 - )

http://thrift.apache.org/

另一方面,它会发送二进制数据,因此您不必将数字转换为字符串,反之亦然。如果您的大部分数据都不是字符串,那么这样做要快得多。

答案 3 :(得分:6)

使用Boost序列化库:

http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/index.html

这可能是最强大,通用,可靠,易于使用甚至跨平台的方式来完成此类任务。

答案 4 :(得分:5)

正如其他人已经说过的那样,使用某种序列化库将提供最强大(也可能是最简单的维护)解决方案。但是,如果您真的想要自己实现它,那么以下显示了如何处理它的“想法”。以下分配缓冲区,然后使用employees矢量内容填充它。假设变量c属于company类型。

有些注意事项:

  • 该示例使用缓冲区为单个发送加载多个条目。使用UDP时,通常不希望一次发送一个条目,因为每个条目会产生一个数据包。
  • 存储在缓冲区中的第一个值是项目数。实际上,可能需要一些附加信息(例如,数据类型)。否则,接收者不一定知道它得到了什么。
  • 将char数据复制并以null结尾。另一种方法是使用长度为数据加前缀并放弃空终止符。
  • 将整数值存储在4字节边界对齐的缓冲区中可能是明智的(取决于系统)。
  • htonl用于以网络字节顺序存储整数值。接收端应使用ntohl将其读出来。

简单而非常不完整的例子:

   // allocate buffer to store all the data for a send.  In a real world
   // this would need to be broken up into appropriately sized chunks
   // to avoid difficulties with UDP packet fragmentation.

   // This likely over-allocates because the structure likely has padding
   char *buf = new char[ sizeof( uint32_t ) +   // for total number of entries
                  sizeof( person ) * c.employees.size() ];  // for each entry

   char *pos = buf;
   // Indicate how many are being sent
   *(uint32_t*)pos = htonl( c.employees.size() );
   pos += sizeof uint32_t;
   for ( vector<person>::iterator pi = c.employees.begin();
         pi != c.employees.end(); pi++ )
      {
      *(uint32_t*)pos = htonl( pi->age );
      pos += sizeof uint32_t;
      strcpy( pos, pi->name );
      pos += strlen( pi->name ) + 1;
      }

   send( 0, buf, (int)( pos - buf ), 0 );


   delete [] buf;

   // The receiving end would then read the number of items and 
   // reconstruct the structure.