Это мой первый вопрос здесь и первый самостоятельный проект. У меня есть клиент (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
Если вы посмотрите на необработанные шестнадцатеричные значения для dWindDirection
, они будут такими:
270: 0x00 0x00 0x00 0x00 0x00 0xe0 0x70 0x40
1.2168372663711258e-309: 0x00 0x00 0x00 0x00 0x00 0xe0 0x00 0x00
Таким образом, два последних байта вашего пакета устанавливаются равными 0 вместо реального значения.
Похоже, может быть проблема с кодом чтения вашего сокета. Я бы начал с проверки данных в сетевом соединении, используя, например. Wireshark. Если там все правильно, установите точку останова в коде приема и проверьте размеры и содержимое буфера.
@Nbreen Обработка кода pktSize
сбивает с толку. Кажется, что сначала у вас есть сообщение длиной pktSize
байт, затем вы добавляете префикс длины, а затем продолжаете отправлять pktSize
байт в сеть, хотя добавленное сообщение теперь должно быть длиннее. Может быть, именно поэтому последние 2 байта отбрасываются?
мое понимание буферов протокола заключается в том, что вызовы выходного потока и сериализации не должны изменять длину буфера. Не могли бы вы предложить назначить размер пакета после сериализации?
@Nbreen Я не могу следовать логике кода (это похоже на слишком сложную повторную реализацию SerializeDelimitedToCodedStream()
?), но мне просто кажется странным, что сначала у вас есть сообщение длины pktSize
, затем вы добавляете префикс и каким-то образом его размер не изменится?
Как сказал jpa в комментариях, я не учитывал префикс, который я добавлял к пакету, поэтому, хотя размер данных был правильным, он не учитывал 4-байтовый заголовок, который я добавлял. Однако, когда сервер читал пакет, он учитывал общий размер и добавленный заголовок, поэтому я всегда был короче на 4 байта при декодировании пакета на стороне сервера, что давало мне значение мусора для последнего поля объекта. В клиентском коде добавление 4 (размер заголовка) к pktSize устранило проблему.
Используя TCPdump, я смог просмотреть пакеты, когда они поступают на сервер, и вы правы, последнее поле равно 0, насколько я могу судить. Есть идеи, почему? В моем выводе из исходного сообщения я знаю, что поле покидает клиент правильно.