Буфер протокола, искажающий данные

Это мой первый вопрос здесь и первый самостоятельный проект. У меня есть клиент (C++ в Windows 10), использующий SimConnect SDK для извлечения данных из симулятора полета Prepar3D, и сервер (C в Ubuntu), который получает эти данные. Я использую сокеты и буферы протоколов. Клиент получает данные от сима, сопоставляет их с буфером протокола и отправляет. Сервер получает его из сокета, декодирует буфер протокола и затем обрабатывает его. Проблема в том, что последнее поле (dWindDirection) всегда повреждено на принимающей стороне. Я не совсем уверен, как/почему он поврежден, но он непротиворечив, поскольку один и тот же ввод в сокет всегда приводит к одному и тому же поврежденному выводу из сокета. Я следовал Буфер протокола через сокет в C++ для использования буферов протокола через сокет. Я включил свой код ниже, а также вывод.

Исходный код также можно найти здесь. https://github.com/nbreen/Simconnect-Data-Client

Клиентская функция, которая отправляет протобафф

DWORD WINAPI createProto(LPVOID lParam) {
    ObjectData* toConvert = (ObjectData*)lParam;
    fopen_s(&dataOut,"dataOut.txt", "a+");
    fprintf(dataOut, "Before serialiing \n");
    logData(toConvert);
    simConnect::simData convertedData;
    std::string toStr(toConvert->szTitle);

    convertedData.set_sztitle(toStr);
    convertedData.set_dabsolutetime(toConvert->dAbsoluteTime);
    convertedData.set_dtime(toConvert->dTime);
    convertedData.set_usimonground(toConvert->uSimOnGround);
    convertedData.set_daltitude(toConvert->dAltitude);
    convertedData.set_dheading(toConvert->dHeading);
    convertedData.set_dspeed(toConvert->dSpeed);
    convertedData.set_dverticalspeed(toConvert->dVerticalSpeed);
    convertedData.set_dgpseta(toConvert->dGpsEta);
    convertedData.set_dlatitude(toConvert->dLatitude);
    convertedData.set_dlongitude(toConvert->dLongitude);
    convertedData.set_dsimtime(toConvert->dSimTime);
    convertedData.set_dtemperature(toConvert->dTemperature);
    convertedData.set_dairpressure(toConvert->dPressure);
    convertedData.set_dwindvelocity(toConvert->dWindVelocity);
    convertedData.set_dwinddirection(toConvert->dWindDirection);

    printf("Size after serializing is %ld\n", convertedData.ByteSizeLong());
    long pktSize = convertedData.ByteSizeLong();
    fprintf(dataOut, "After serializing before socket\n%s\n\n", convertedData.DebugString().c_str());
    char* pkt = new char[pktSize];
    google::protobuf::io::ArrayOutputStream aos(pkt, pktSize);
    google::protobuf::io::CodedOutputStream* coded_pkt = new google::protobuf::io::CodedOutputStream(&aos);
    coded_pkt->WriteVarint64(convertedData.ByteSizeLong());
    convertedData.SerializeToCodedStream(coded_pkt);
    int iResult = send(clientSocket, pkt, pktSize, 0);
    printf("Sent bytes %d\n", iResult);
    fclose(dataOut);

    return 0;
}

Серверная функция, которая получает и обрабатывает протобафф

while(1) {
        result = recv(client, sizeBuff, 4, MSG_PEEK);
        printf("Receive is %d\n", result);
        if (result == -1) {
            printf("Error receiving data with byteSize\n");
            break;
        }else if (result == 0) {
            break;
        }

        if (result > 0) {
            printf("First read byte count is %d\n", result);
            readMessage(client, readHeader(sizeBuff));
        }
    }

google::protobuf::uint64 readHeader(char *buf) {
  google::protobuf::uint64 size;
  google::protobuf::io::ArrayInputStream ais(buf,4);
  google::protobuf::io::CodedInputStream coded_input(&ais);
  coded_input.ReadVarint64(&size);//Decode the HDR and get the size
  std::cout<<"size of payload is "<<size<<std::endl;
  return size;
}

void readMessage(int csock, google::protobuf::uint64 siz) {
  int bytecount;
  simConnect::simData payload;
  char buffer [siz+4];//size of the payload and hdr
  //Read the entire buffer including the hdr
  if ((bytecount = recv(csock, (void *)buffer, 4+siz, 0))== -1){
                fprintf(stderr, "Error receiving data %d\n", errno);
        }
  std::cout<<"Second read byte count is "<<bytecount<<std::endl;
  //Assign ArrayInputStream with enough memory 
  google::protobuf::io::ArrayInputStream ais(buffer,siz+4);
  google::protobuf::io::CodedInputStream coded_input(&ais);
  //Read an unsigned integer with Varint encoding, truncating to 32 bits.
  coded_input.ReadVarint64(&siz);
  //After the message's length is read, PushLimit() is used to prevent the CodedInputStream 
  //from reading beyond that length.Limits are used when parsing length-delimited 
  //embedded messages
  google::protobuf::io::CodedInputStream::Limit msgLimit = coded_input.PushLimit(siz);
  //De-Serialize
  payload.ParseFromCodedStream(&coded_input);
  //Once the embedded message has been parsed, PopLimit() is called to undo the limit
  coded_input.PopLimit(msgLimit);
  //Print the message
  //std::cout<<"Message is "<<payload.DebugString();
  FILE *outFile = fopen("out.txt", "a+");
  /*std::string strPkt(buffer);
  payload.ParseFromString(strPkt);*/
  fprintf(outFile, "From socket\n%s\n\n", payload.DebugString().c_str());
  fclose(outFile);
  cudaProcess(payload);
}

Выход клиента

Before serialiing 
Title: F-22 Raptor - 525th Fighter Squadron
Absolute Time: 63631767652.278290 seconds
Zulu Time: 68452.281250 Seconds
Sim On Ground: 0 
Altitude: 11842.285630 Feet
Heading: 3.627703 Radians
Speed: 426.008209 Knots
Vertical Speed: 596.607849 Feet Per Second
GPS ETA: 0.000000 Seconds
Latitude: 30.454685 Degrees
Longitude: -86.525197 Degrees
Sim Time: 2996.388142 Seconds
Temperature: -8.145923 Celsius
Air Pressure: 648.718994 Millibars
Wind Velocity: 36.988354 Feet Per Second
Wind Direction: 270.000000 Degrees


After serializing before socket
szTitle: "F-22 Raptor - 525th Fighter Squadron"
dAbsoluteTime: 63631767652.27829
dTime: 68452.28125
usimOnGround: 0
dAltitude: 11842.285629708613
dHeading: 3.6277025313026936
dSpeed: 426.00820922851562
dVerticalSpeed: 596.60784912109375
dGpsEta: 0
dLatitude: 30.454685493870045
dLongitude: -86.525197127202063
dSimTime: 2996.388142343636
dTemperature: -8.1459226608276367
dAirPressure: 648.718994140625
dWindVelocity: 36.988353729248047
dWindDirection: 270

Выход сервера

From socket
szTitle: "F-22 Raptor - 525th Fighter Squadron"
dAbsoluteTime: 63631767652.27829
dTime: 68452.28125
usimOnGround: 0
dAltitude: 11842.285629708613
dHeading: 3.6277025313026936
dSpeed: 426.00820922851562
dVerticalSpeed: 596.60784912109375
dGpsEta: 0
dLatitude: 30.454685493870045
dLongitude: -86.525197127202063
dSimTime: 2996.388142343636
dTemperature: -8.1459226608276367
dAirPressure: 648.718994140625
dWindVelocity: 36.988353729248047
dWindDirection: 1.2168372663711258e-309 <-- This is always the corrupt value when Wind direction is 270
Стоит ли изучать PHP в 2023-2024 годах?
Стоит ли изучать PHP в 2023-2024 годах?
Привет всем, сегодня я хочу высказать свои соображения по поводу вопроса, который я уже много раз получал в своем сообществе: "Стоит ли изучать PHP в...
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
Поведение ключевого слова "this" в стрелочной функции в сравнении с нормальной функцией
В JavaScript одним из самых запутанных понятий является поведение ключевого слова "this" в стрелочной и обычной функциях.
Приемы CSS-макетирования - floats и Flexbox
Приемы CSS-макетирования - floats и Flexbox
Здравствуйте, друзья-студенты! Готовы совершенствовать свои навыки веб-дизайна? Сегодня в нашем путешествии мы рассмотрим приемы CSS-верстки - в...
Тестирование функциональных ngrx-эффектов в Angular 16 с помощью Jest
В системе управления состояниями ngrx, совместимой с Angular 16, появились функциональные эффекты. Это здорово и делает код определенно легче для...
Концепция локализации и ее применение в приложениях React ⚡️
Концепция локализации и ее применение в приложениях React ⚡️
Локализация - это процесс адаптации приложения к различным языкам и культурным требованиям. Это позволяет пользователям получить опыт, соответствующий...
Пользовательский скаляр GraphQL
Пользовательский скаляр GraphQL
Листовые узлы системы типов GraphQL называются скалярами. Достигнув скалярного типа, невозможно спуститься дальше по иерархии типов. Скалярный тип...
0
0
450
2
Перейти к ответу Данный вопрос помечен как решенный

Ответы 2

Если вы посмотрите на необработанные шестнадцатеричные значения для dWindDirection, они будут такими:

270:                     0x00 0x00 0x00 0x00 0x00 0xe0 0x70 0x40
1.2168372663711258e-309: 0x00 0x00 0x00 0x00 0x00 0xe0 0x00 0x00

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

Похоже, может быть проблема с кодом чтения вашего сокета. Я бы начал с проверки данных в сетевом соединении, используя, например. Wireshark. Если там все правильно, установите точку останова в коде приема и проверьте размеры и содержимое буфера.

Используя TCPdump, я смог просмотреть пакеты, когда они поступают на сервер, и вы правы, последнее поле равно 0, насколько я могу судить. Есть идеи, почему? В моем выводе из исходного сообщения я знаю, что поле покидает клиент правильно.

Nbreen 20.12.2020 07:02

@Nbreen Обработка кода pktSize сбивает с толку. Кажется, что сначала у вас есть сообщение длиной pktSize байт, затем вы добавляете префикс длины, а затем продолжаете отправлять pktSize байт в сеть, хотя добавленное сообщение теперь должно быть длиннее. Может быть, именно поэтому последние 2 байта отбрасываются?

jpa 20.12.2020 08:23

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

Nbreen 20.12.2020 08:31

@Nbreen Я не могу следовать логике кода (это похоже на слишком сложную повторную реализацию SerializeDelimitedToCodedStream()?), но мне просто кажется странным, что сначала у вас есть сообщение длины pktSize, затем вы добавляете префикс и каким-то образом его размер не изменится?

jpa 20.12.2020 08:49
Ответ принят как подходящий

Как сказал jpa в комментариях, я не учитывал префикс, который я добавлял к пакету, поэтому, хотя размер данных был правильным, он не учитывал 4-байтовый заголовок, который я добавлял. Однако, когда сервер читал пакет, он учитывал общий размер и добавленный заголовок, поэтому я всегда был короче на 4 байта при декодировании пакета на стороне сервера, что давало мне значение мусора для последнего поля объекта. В клиентском коде добавление 4 (размер заголовка) к pktSize устранило проблему.

Другие вопросы по теме

Похожие вопросы

Swift memcpy не имеет никакого эффекта при использовании с ZMQ zmq_msg_data
Getter & Setter в проекте C++/C?
Почему метод присваивания одной переменной отличается от присваивания двух отдельных переменных в C++?
Как отключить предупреждение от компилятора VS-Code GCC? (не используйте #pragma)
Вопрос об адресе объекта возвращаемого объекта с типом boost::function &
В С++ 2D-массив медленнее в общих операциях, чем 1D-массив, который ведет себя как 2D-массив?
Как Line Trace/Raycasting работает в игровых движках для тестирования пересечений?
C++, linux - разрешение mmap() запрещено для файла, созданного программой, для созданного файла открыты все разрешения
Ошибка с большим количеством символов при построении буквенной пирамиды
Объект выходит за пределы области действия, и вызывается деструктор, в результате чего указатель, следующий за адресом этого объекта, имеет нечитаемую память для каждого атрибута