UDP Socket跨语言通信获取数据出现垃圾值问题求助
问题:UDP传输JSON后C++使用
std::array<uint8_t, N>存储字符串出现乱码 我用Python编写UDP发送端,将API响应序列化为JSON后发送给C接收端。C解析时,Instruction结构体中使用std::array<uint8_t, 100>这类固定大小数组存储字符串字段,输出会出现垃圾乱码后缀;但替换为std::string则运行正常。
Python发送端代码
# Function to send data via UDP socket def send_data_via_udp(data): # UDP server address and port udp_server_address = ('127.0.0.1', 12343) # Change this to your destination UDP server address and port # Create a UDP socket udp_socket = socket.socket(socket.AF_INET, socket.SOCK_DGRAM) try: # Convert data to JSON string json_data = json.dumps(data) # Send data via UDP socket udp_socket.sendto(json_data.encode(), udp_server_address) print("Data sent via UDP successfully.") except Exception as e: print("Error while sending data via UDP:", e) finally: # Close the UDP socket udp_socket.close() data_to_send = {'path': coordinates, 'distance': distance, 'instruction': instruction, 'time': time}
C++接收端代码
struct Instruction { int type; std::array<uint8_t, 100> instruction; std::array<uint8_t, 100> verbal_transition_alert_instruction; std::array<uint8_t, 50> verbal_succinct_transition_instruction; std::array<uint8_t, 100> verbal_pre_transition_instruction; std::array<uint8_t, 100> verbal_post_transition_instruction; std::vector<std::string> street_names; std::vector<std::string> begin_street_names; double time; double length; double cost; int begin_shape_index; int end_shape_index; std::array<uint8_t, 50> travel_mode; std::array<uint8_t, 50> travel_type; }; // Structure to store the received data struct ReceivedData { std::vector<std::pair<double, double>> path; double distance; std::vector<Instruction> instructions; double time; }; // Function to parse JSON data and extract the required fields ReceivedData parseJsonData(const std::string &jsonData) { ReceivedData data; Json::Value root; Json::Reader reader; // Parse JSON if (!reader.parse(jsonData, root)) { std::cerr << "Error parsing JSON data: " << reader.getFormattedErrorMessages() << std::endl; return data; // Return empty data in case of parsing error } try { // Extract path coordinates const Json::Value &pathArray = root["path"]; for (const auto &coord : pathArray) { double lat = coord[0].asDouble(); double lon = coord[1].asDouble(); data.path.push_back(std::make_pair(lat, lon)); } // Extract distance data.distance = root["distance"].asDouble(); // Extract instructions const Json::Value &instructionArray = root["instruction"]; for (const auto &instr : instructionArray) { Instruction instruction; instruction.type = instr["type"].asInt(); // Convert JSON strings to std::array<uint8_t, 100> std::string instructionStr = instr["instruction"].asString(); std::copy(instructionStr.begin(), instructionStr.end(), instruction.instruction.begin()); std::string verbalTransitionAlertStr = instr["verbal_transition_alert_instruction"].asString(); std::copy(verbalTransitionAlertStr.begin(), verbalTransitionAlertStr.end(), instruction.verbal_transition_alert_instruction.begin()); std::string verbalSuccinctTransitionStr = instr["verbal_succinct_transition_instruction"].asString(); std::copy(verbalSuccinctTransitionStr.begin(), verbalSuccinctTransitionStr.end(), instruction.verbal_succinct_transition_instruction.begin()); std::string verbalPreTransitionStr = instr["verbal_pre_transition_instruction"].asString(); std::copy(verbalPreTransitionStr.begin(), verbalPreTransitionStr.end(), instruction.verbal_pre_transition_instruction.begin()); instruction.verbal_post_transition_instruction = stringToArray(instr["verbal_post_transition_instruction"].asString()); std::copy(verbalPostTransitionStr.begin(), verbalPostTransitionStr.end(), instruction.verbal_post_transition_instruction.begin()); instruction.verbal_post_transition_instruction[verbalPostTransitionStr.size()] = '\0'; // for(size_t i = 0; i<verbalPostTransitionStr.size() && i<100; ++i){ // instruction.verbal_post_transition_instruction[i] = static_cast<uint8_t>(verbalPostTransitionStr[i]); // } // std::copy(verbalPostTransitionStr.begin(), verbalPostTransitionStr.end(), instruction.verbal_post_transition_instruction.begin()); //instruction.verbal_post_transition_instruction[verbalPostTransitionStr.size()] = '\0'; // Ensure null-termination // if (verbalPostTransitionStr.size() < 100) // { // instruction.verbal_post_transition_instruction[verbalPostTransitionStr.size()] = '\0'; // } for (const auto &street : instr["street_names"]) { instruction.street_names.push_back(street.asString()); } for (const auto &begin_street : instr["begin_street_names"]) { instruction.begin_street_names.push_back(begin_street.asString()); } instruction.time = instr["time"].asDouble(); instruction.length = instr["length"].asDouble(); instruction.cost = instr["cost"].asDouble(); instruction.begin_shape_index = instr["begin_shape_index"].asInt(); instruction.end_shape_index = instr["end_shape_index"].asInt(); std::string travelModeStr = instr["travel_mode"].asString(); std::copy(travelModeStr.begin(), travelModeStr.end(), instruction.travel_mode.begin()); std::string travelTypeStr = instr["travel_type"].asString(); std::copy(travelTypeStr.begin(), travelTypeStr.end(), instruction.travel_type.begin()); data.instructions.push_back(instruction); } // Extract time data.time = root["time"].asDouble(); } catch (const std::exception &e) { std::cerr << "Error extracting data from JSON: " << e.what() << std::endl; } return data; } int main() { // Create a UDP socket int udpSocket = socket(AF_INET, SOCK_DGRAM, 0); if (udpSocket < 0) { std::cerr << "Error in socket creation" << std::endl; return -1; } // Bind the socket to an address and port struct sockaddr_in serverAddr; serverAddr.sin_family = AF_INET; serverAddr.sin_addr.s_addr = INADDR_ANY; // Bind to any address serverAddr.sin_port = htons(RECEIVER_PORT); if (bind(udpSocket, (struct sockaddr *)&serverAddr, sizeof(serverAddr)) < 0) { std::cerr << "Bind failed" << std::endl; return -1; } struct sockaddr_in senderAddr; socklen_t senderSize = sizeof(senderAddr); char buffer[45008]; // Increase buffer size to accommodate larger JSON data while (true) { std::cout << "Waiting for data..." << std::endl; // Wait for data to receive ssize_t bytesReceived = recvfrom(udpSocket, buffer, sizeof(buffer), 0, (struct sockaddr *)&senderAddr, &senderSize); if (bytesReceived < 0) { std::cerr << "Error in recvfrom()" << std::endl; break; } std::string receivedData(buffer, bytesReceived); std::cout << "Received data: " << receivedData << std::endl; // Parse received JSON data ReceivedData parsedData = parseJsonData(receivedData); // Print extracted data std::cout << "Path coordinates:" << std::endl; for (const auto &coord : parsedData.path) { std::cout << "(" << coord.first << ", " << coord.second << ")" << std::endl; } std::cout << "Distance: " << parsedData.distance << std::endl; std::cout << "Instructions:" << std::endl; for (const auto &instr : parsedData.instructions) { std::cout << "Type: " << instr.type << std::endl; std::cout << "Instruction: "; for (const auto &value : instr.instruction) { std::cout << static_cast<char>(value); } std::cout << std::endl; std::cout << "Verbal Post Transition Instruction: "; for (const auto &value : instr.verbal_post_transition_instruction) { std::cout << static_cast<char>(value); } std::cout << std::endl; // Print other instruction fields as needed } std::cout << "Time: " << parsedData.time << std::endl; } // Close the socket close(udpSocket); return 0; }
异常输出
Instruction: Bear left to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 400 meters.��"�zU,��,����3 Type: 16 Instruction: Bear left to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 30 meters.��"�zU,��,����3 Type: 24 Instruction: Keep left to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 200 meters.��"�zU,��,����3 Type: 23 Instruction: Keep right to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 100 meters.��"�zU,��,����3 Type: 23 Instruction: Keep right to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 2.5 kilometers.��"�zU,��,����3 Type: 23 Instruction: Keep right to stay on Mehrauli Gurgaon Road/NH148A. Verbal Post Transition Instruction: Continue for 200 meters.rs.��"�zU,��,����3 Type: 23 Instruction: Keep right to take Mehrauli-Gurgaon Road.ad/NH148A. Verbal Post Transition Instruction: Continue for 80 meters.rs.��"�zU,��,����3 Type: 23 Instruction: Keep right to stay on Mehrauli-Gurgaon Road.NH148A. Verbal Post Transition Instruction: Continue for 1 kilometer.s.��"�zU,��,����3 Type: 23 Instruction: Keep right to stay on Mehrauli-Gurgaon Road.NH148A. Verbal Post Transition Instruction: Continue for 3 kilometers..��"�zU,��,����3
解决方案
问题根源
std::array<uint8_t, N>是固定大小数组,默认初始化不会清零,数组内会残留内存垃圾值。- 复制字符串时仅拷贝有效字符,未统一添加字符串终止符
\0;输出时遍历整个数组,导致垃圾值被打印。 verbal_post_transition_instruction的处理逻辑混乱,存在未定义变量verbalPostTransitionStr,且重复赋值覆盖了终止符设置。
修复步骤
1. 编写通用字符串转固定数组函数
确保字符串被正确拷贝并添加终止符,同时处理字符串超长的情况:
template <size_t N> void stringToFixedArray(const std::string& str, std::array<uint8_t, N>& arr) { // 清空数组,避免残留垃圾值 arr.fill(0); // 最多拷贝N-1个字符,留位置放终止符 size_t copyLen = std::min(str.size(), N - 1); std::copy(str.begin(), str.begin() + copyLen, arr.begin()); // 强制添加终止符 arr[copyLen] = '\0'; }
2. 替换解析函数中的赋值逻辑
把所有std::copy替换为上述通用函数,修复verbal_post_transition_instruction的错误逻辑:
// 替换原有的std::copy逻辑 stringToFixedArray(instructionStr, instruction.instruction); stringToFixedArray(verbalTransitionAlertStr, instruction.verbal_transition_alert_instruction); stringToFixedArray(verbalSuccinctTransitionStr, instruction.verbal_succinct_transition_instruction); stringToFixedArray(verbalPreTransitionStr, instruction.verbal_pre_transition_instruction); // 修复verbal_post_transition_instruction的处理 std::string verbalPostTransitionStr = instr["verbal_post_transition_instruction"].asString(); stringToFixedArray(verbalPostTransitionStr, instruction.verbal_post_transition_instruction); // travel_mode和travel_type同理 stringToFixedArray(travelModeStr, instruction.travel_mode); stringToFixedArray(travelTypeStr, instruction.travel_type);
3. 优化输出逻辑
输出时以终止符为结束标志,不再遍历整个数组:
// 替换原有的遍历输出 std::cout << "Instruction: "; for (size_t i = 0; i < instruction.instruction.size() && instruction.instruction[i] != '\0'; ++i) { std::cout << static_cast<char>(instruction.instruction[i]); } std::cout << std::endl; std::cout << "Verbal Post Transition Instruction: "; for (size_t i = 0; i < instruction.verbal_post_transition_instruction.size() && instruction.verbal_post_transition_instruction[i] != '\0'; ++i) { std::cout << static_cast<char>(instruction.verbal_post_transition_instruction[i]); } std::cout << std::endl;
4. 可选方案:直接改用std::string
如果没有必须使用固定数组的需求,直接将结构体中的std::array<uint8_t, N>替换为std::string是最简洁可靠的方案,无需手动处理终止符和数组大小问题。
内容的提问来源于stack exchange,提问作者user23369001
相关产品推荐
相关产品推荐

