#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-разрядный, и вы должны понять, что происходит.