协议缓冲区损坏数据
Protocol Buffer Corrupting Data
这是我在这里的第一个问题,也是我自己的第一个项目。我有一个客户端(Windows 10 上的 C++)使用 SimConnect SDK 从 Prepar3D 飞行模拟器和接收此数据的服务器(Ubuntu 上的 C)提取数据。我正在使用套接字和协议缓冲区。客户端从 sim 接收数据,将其映射到协议缓冲区并发送。服务器从套接字接收它,解码协议缓冲区,然后处理它。问题是最后一个字段 (dWindDirection) 在接收端总是损坏。我不完全确定 how/why 它是否已损坏,但它是一致的,因为套接字的相同输入总是导致套接字的相同损坏输出。我遵循 Protocol Buffer over socket in C++ 在套接字上使用协议缓冲区。我在下面包含了我的代码以及输出。
源代码也可以在这里找到。 https://github.com/nbreen/Simconnect-Data-Client
发送protobuff的客户端函数
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;
}
接收并处理protobuff的服务器函数
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。如果那里是正确的,请在您的接收代码中设置一个断点并检查大小和缓冲区内容。
正如 jpa 在评论中所说,我没有考虑我附加到数据包的前缀,所以尽管数据的大小是正确的,但它没有考虑我附加的 4 字节 header .但是,当服务器读取数据包时,它会考虑总大小和附加的 header 所以在服务器端解码数据包时我总是少 4 个字节,最后一个字段的垃圾值的 object。在客户端代码中添加 4(header 的大小)到 pktSize 解决了这个问题。
这是我在这里的第一个问题,也是我自己的第一个项目。我有一个客户端(Windows 10 上的 C++)使用 SimConnect SDK 从 Prepar3D 飞行模拟器和接收此数据的服务器(Ubuntu 上的 C)提取数据。我正在使用套接字和协议缓冲区。客户端从 sim 接收数据,将其映射到协议缓冲区并发送。服务器从套接字接收它,解码协议缓冲区,然后处理它。问题是最后一个字段 (dWindDirection) 在接收端总是损坏。我不完全确定 how/why 它是否已损坏,但它是一致的,因为套接字的相同输入总是导致套接字的相同损坏输出。我遵循 Protocol Buffer over socket in C++ 在套接字上使用协议缓冲区。我在下面包含了我的代码以及输出。
源代码也可以在这里找到。 https://github.com/nbreen/Simconnect-Data-Client
发送protobuff的客户端函数
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;
}
接收并处理protobuff的服务器函数
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。如果那里是正确的,请在您的接收代码中设置一个断点并检查大小和缓冲区内容。
正如 jpa 在评论中所说,我没有考虑我附加到数据包的前缀,所以尽管数据的大小是正确的,但它没有考虑我附加的 4 字节 header .但是,当服务器读取数据包时,它会考虑总大小和附加的 header 所以在服务器端解码数据包时我总是少 4 个字节,最后一个字段的垃圾值的 object。在客户端代码中添加 4(header 的大小)到 pktSize 解决了这个问题。