Передача структуры через сокет TCP в C

#c #sockets

#c #сокеты

Вопрос:

Я пишу небольшое клиент-серверное приложение на C.

На стороне клиента у меня есть одна структура, подобная,

 #pragma pack(1)   // this helps to avoid serialization while sending over network. 
typedef struct _viewBoxClient_Info
{
  unsigned long viewBoxID;
  int bRT_flag;
  int nFrameNum;
  char frameData[1000];   
}viewBoxClient_info_send ;
#pragma pack(0)   // turn packing off 
  

и заполнение переменной как,

 struct _viewBoxClient_Info client_info;
client_info.bRT_flag= 10;/*0 for false, 1 for true*/
client_info.viewBoxID=10000;
memcpy(client_info.frameData,buf,sizeof(client_info.frameData)); //char buf[] data is "is 1st line"
client_info.nFrameNum=1;
  

и отправка на сервер с помощью следующей функции,

 send(sock, (char *)amp;client_info, bytesRead, 0);
  

На стороне сервера у меня есть одна структура, подобная (такая же, как структура на стороне клиента),

 #pragma pack(1)   // this helps to avoid serialization while sending over network.
typedef struct _lclviewBoxClient_Info
{
 unsigned long viewBoxID;
 int bRT_flag;
 int nFrameNum;
 char frameData[1000];
}viewBoxClient_info_receive ;
#pragma pack(0)   // turn packing off
  

и получение сообщения и печать на экране в виде,

 viewBoxClient_info_receive lcl_viewBox;
ssize_t bytesReceived = recv( *nfd, amp;lcl_viewBox, sizeof(struct _lclviewBoxClient_Info), 0);
printf("nlcl_viewBox.bRT_flag:%dn",lcl_viewBox.bRT_flag);
printf("lcl_viewBox.nFrameNum:%dn",lcl_viewBox.nFrameNum);
printf("lcl_viewBox.frameData:%sn",lcl_viewBox.frameData);
  

Ввод / вывод на экране сервера,

  lcl_viewBox.bRT_flag:1
 lcl_viewBox.nFrameNum:1936287860
 lcl_viewBox.frameData: is 1st line
  

Я не знаю, что именно происходит на моей стороне сервера. Я отправляю 10 для bRT_flag от клиента и получаю 1 на сервере. Также отправка nFrameNum = 1 от клиента и получение в виде nFrameNum: 1936287860 на сервере. frameData также на стороне клиента я отправляю «Это 1-я строка», а на сервере получаю только «это 1-я строка».
Кто-нибудь поможет избавиться от этой проблемы?

спасибо и с уважением,

Sri

Ответ №1:

Не должно ли это:

 send(sock, (char *)amp;client_info, bytesRead, 0);
  

быть:

 send(sock, (char *)amp;client_info, sizeof( client_info ), 0);
  

И вы должны проверить возвращаемое значение send () и особенно recv() — нет гарантии, что вызов recv извлечет вашу структуру за один раз.

Ответ №2:

Отправка необработанных структур, подобных этой, — очень плохая идея.

Вам приходится иметь дело с порядком байтов (endianness), упаковкой (что все еще может быть проблемой даже с #pragma pack ), и еще одна проблема, с которой вы сталкиваетесь, заключается в том, что размеры типов, таких как ‘int’, могут различаться на разных платформах.

Я бы порекомендовал библиотеку сериализации, такую как Google Protocol Buffers, чтобы помочь вам в этом. Если вы все еще хотите сделать это самостоятельно, вам нужно найти такие функции, как htonl для преобразования целочисленных типов в сетевой порядок байтов, и начать использовать примитивы фиксированного размера, такие uint32_t как etc.

Вам также следует прекратить отправку всей структуры сразу и вместо этого написать вспомогательные функции, такие как writeClientStruct(int sock, struct client_struct *), которые будут отправлять значение каждого элемента отдельно, чтобы избежать проблем с упаковкой между платформами.

Комментарии:

1. Я новичок в этом дополнении. Не могли бы вы, пожалуйста, рассказать мне о сериализации и десериализации на простом примере?

Ответ №3:

Одним из объяснений было бы то, что ваш клиент и сервер по-разному интерпретируют размер «unsigned long». (Выполняется на разных машинах или с разными параметрами компилятора, другие являются 32-разрядными, а другие 64-разрядными).

Это объяснило бы симптомы:

  • bRT_flag сдвинут на 32 бита в nFrameNum
  • nFrameNum, содержащий первые 32 бита из frameData (1936287860 -> 0x73696874 -> «siht» -> с заменой в конце «this»)

Тривиальной проверкой для этого было бы проверить sizeof (структура) или sizeof (длина без знака), чтобы увидеть, совпадают ли они.

Пожалуйста, смотрите сообщение Майка Уэллера о том, как это правильно исправить.

Комментарии:

1. если есть проблема с «unsigned long», то почему frameData не отображается полностью?

2. Потому что также было сдвинуто начало frameData. Все, что находится после viewBOXID, сдвинуто на 4 байта: bRT_flag содержит данные nFramenum, nFramenum содержит первые 4 байта из frameData, а в frameData отсутствуют первые 4 байта «this».

3. Распечатайте шестнадцатеричный дамп памяти, которую вы отправляете, а затем интерпретируйте viewBOXID либо как 64-разрядный, либо как 32-разрядный, и вы должны понять, что происходит.