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

JJ *_*Liu 11 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;
};
Run Code Online (Sandbox Code Playgroud)

例如,我想要send/recv整个company使用套接字(UDP).所以,发送和recv一次.

我怎样才能做到这一点?你能给我一些代码吗?如何发送所有内容并阅读所有内容.

谢谢!

Omn*_*ous 18

您的问题的措辞表明您正在寻找的是:

company foo;
send(sockfd, &foo, sizeof(foo), 0); // DO NOT do this
Run Code Online (Sandbox Code Playgroud)

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

它对个人personcar物体有用.它们不包含任何指针,因此它们的内存包含所有相关值

在发送方:

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

在接收方:

person joe;
recv(sockfd, &joe, sizeof(joe), 0);
Run Code Online (Sandbox Code Playgroud)

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

真的,你应该像这里所提出的每个人一样使用序列化框架.我会建议谷歌协议缓冲区或其他人已经链接到的Boost序列化框架.但还有很多其他的.

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


Ser*_*lov 8

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


Ale*_*lke 7

如果你要做很多这些,你可能想要看看Thrift.它将自动生成所有必要的代码,以便为您序列化所有结构,因此您无需手动执行此操作.

他们支持字符串也非常实用.

哦! 它是免费的.8-)

http://thrift.apache.org/

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


vit*_*kot 6

使用Boost序列化库:

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

对于此类任务而言,这可能是最强大,通用,可靠,易于使用甚至跨平台的方式.


Mar*_*ins 6

正如其他人已经说过的那样,使用某种序列化库将提供最强大(也可能是最简单的维护)解决方案.但是,如果您真的想要自己实现它,那么以下显示了如何处理它的"想法".以下分配缓冲区,然后使用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.
Run Code Online (Sandbox Code Playgroud)