C ++ Как отправить структуры через сокет? - PullRequest
11 голосов
/ 15 декабря 2011

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

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?Как отправить все и прочитать все.

Спасибо!

Ответы [ 5 ]

17 голосов
/ 15 декабря 2011

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

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 или Boost serialization Framework , с которыми уже связались другие люди.Но есть и много других.

Еще одна инфраструктура сериализации, о которой следует упомянуть, потому что она невероятно быстрая (почти такая же быстрая, как прямая выгрузка образа памяти структуры в сокет): Cap'n Proto .

8 голосов
/ 15 декабря 2011

Взгляните на буферы протокола Google http://code.google.com/apis/protocolbuffers/ в качестве альтернативы Boost сериализации.

7 голосов
/ 15 декабря 2011

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

Они также поддерживают строки, что очень практично.

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

http://thrift.apache.org/

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

6 голосов
/ 15 декабря 2011

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

Несколько замечаний:

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

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

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

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...