You need to enable JavaScript to run this app.
优惠活动
大模型
产品
解决方案
定价
更多

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

解决方案

问题根源

  1. std::array<uint8_t, N>是固定大小数组,默认初始化不会清零,数组内会残留内存垃圾值。
  2. 复制字符串时仅拷贝有效字符,未统一添加字符串终止符\0;输出时遍历整个数组,导致垃圾值被打印。
  3. 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

相关产品推荐
方舟 Agent Plan

超全模态模型 × Harness 升级,最新支持 Deepseek-V4.1-Flash、GLM-5.3 系列、Doubao-Seedream-5.0-pro、Kimi-K3 (部分), 限时 9.9 元起

最近更新时间:2026.06.28 17:57:03