C++ как отправить структуры через сокет?
скажем, у меня есть структура:
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). Итак, отправьте и recv один раз.
как я могу это сделать? Не могли бы вы дать мне код синппет? Как отправить все и прочитать все.
спасибо!
5 ответов
взгляните на буферы протокола Googlehttp://code.google.com/apis/protocolbuffers/ в качестве альтернативы lite для повышения сериализации.
формулировка вашего вопроса предполагает, что то, что вы ищете, это:
company foo;
send(sockfd, &foo, sizeof(foo), 0); // DO NOT do this
это в основном сбросит всю память company
struct в сокет. Это НЕ БУДЕТ РАБОТАТЬ в этом случае. И даже когда это вроде как работает, это действительно плохая идея. Причина, по которой это не сработает, заключается в том, что vector
s не содержат данных напрямую. Они указывают на него. Это означает, что когда вы сбрасываете структуру, содержащую векторы, в сокет, вы будете сбрасывание указателей на память, но не на то, на что указывают. Это приведет к (в лучшем случае) сбоям на принимающей стороне.
это будет работать для отдельных 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 или повысить структуру сериализации что другие люди уже связаны. Но есть много других.
другой фреймворк сериализации, который следует упомянуть, потому что он невероятно быстр (почти так же быстро, как прямой сброс образа памяти структуры в сокет), является капитан Прото.
Если вы должны сделать много из них, вы можете посмотреть в бережливости. Он автоматически сгенерирует весь необходимый код для сериализации всех структур, поэтому вам не придется делать это вручную.
Они поддерживают строки тоже очень практично.
о! И это бесплатно. 8-)
другое дело, он отправляет двоичные данные как таковые, поэтому вам не придется преобразовывать числа в строки и наоборот. Это намного быстрее сделать, если большинство ваших данных не являются строками.
используйте библиотеку сериализации Boost:
http://www.boost.org/doc/libs/1_48_0/libs/serialization/doc/index.html
Это, пожалуй, самый надежный, универсальный, надежный, простой в использовании и даже кросс-платформенный способ для таких задач.
как уже говорили другие, использование какой-то библиотеки сериализации обеспечит наиболее надежное (и, вероятно, самое простое в обслуживании) решение. Если, однако, вы действительно хотите реализовать все это самостоятельно, то следующее показывает "идею" того, как, возможно, подойти к нему. Следующее выделяет буфер, а затем заполняет его содержимым вектора 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.