С++ Как отправить структуры через сокет?

Скажем, у меня есть структура:

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

struct car
{
    int locationX;
    int locationY;
};

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

Например, я хочу send/recv целое company использовать сокет (UDP). Итак, отправьте и верните один раз.

Как я могу это сделать? Не могли бы вы дать мне какой-то код sinppet? Как отправить все и прочитать все.

Спасибо!

Ответ 2

В формулировке вашего вопроса предполагается, что вы ищете следующее:

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

В основном это сбрасывает всю память структуры company в ваш сокет. Этот НЕ РАБОТАЕТ в этом случае. И даже когда это работает, это действительно плохая идея. Причина, по которой он не будет работать, заключается в том, что vector не содержит данные напрямую. Они указывают на это. Это означает, что когда вы выгружаете структуру, содержащую векторы, в сокет, вы будете указывать указатели на память, но не на то, что указано. Это приведет к (в лучшем случае) сбоям на принимающей стороне.

Это будет работать для отдельных объектов person или car. Они не содержат указателей, поэтому их память содержит все соответствующие значения

На стороне отправки:

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. Или один из них находится на компьютере Windows, скомпилированном с Visual Studio, а другой - на машине Linux, скомпилированной с помощью gcc. Или, возможно, кто-то изменил некоторые флагов компилятора, которые по-разному меняют структуру структуры по умолчанию. Любое количество причин.

На самом деле, вы должны использовать схему сериализации, как все здесь предложили. Я бы предложил буферы протокола Google или растровую структуру сериализации, с которой уже связаны другие люди. Но есть и другие.

Ответ 3

Если вы хотите сделать много из них, вы можете посмотреть в "Трэйфте". Он автоматически сгенерирует весь необходимый код для сериализации всех структур для вас, поэтому вам не придется делать это вручную.

Они поддерживают строки слишком практично.

О! И это бесплатно. 8 -)

http://thrift.apache.org/

Другое дело, он отправляет двоичные данные как таковые, поэтому вам не придется преобразовывать числа в строки и наоборот. Это намного быстрее сделать, если большинство ваших данных не являются строками.

Ответ 4

Использовать библиотеку сериализации Boost:

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

Это, вероятно, самый надежный, универсальный, надежный, простой в использовании и даже кросс-платформенный способ для такой задачи.

Ответ 5

Как уже говорили другие, использование какой-либо библиотеки сериализации обеспечит наиболее надежное (и, вероятно, самое простое в обслуживании) решение. Если, однако, вы действительно хотите реализовать все это самостоятельно, то следующее показывает "идею", как, возможно, приблизиться к ней. Следующий выделяет буфер, а затем заполняет его содержимым векторного персонала. Предполагается, что переменная c имеет тип company.

Некоторые примечания:

  • В примере используется буфер для загрузки нескольких записей для одной отправки. В UDP обычно было бы нежелательно отправлять одну запись за раз, поскольку она привела бы к одному пакету на запись.
  • Первое значение, хранящееся в буфере, - это количество элементов. В действительности, вероятно, потребуется некоторая дополнительная информация (например, тип данных). В противном случае получатель не обязательно узнает, что он получает.
  • Данные char копируются и завершаются нулем. Другой способ - префикс данных с длиной и отказ от нулевого терминатора.
  • Возможно, было бы разумно хранить целочисленные значения в буфере, выровненном на 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.