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

Boost::Asio deadline timer导致UDP Socket读取阻塞问题求助

问题分析与修复方案

核心问题定位

从代码和运行日志来看,导致定时器触发后接收功能失效的主要原因有两个:

1. 内存越界访问破坏程序状态

handle_incoming函数中错误地将收到的原始数据强制转换为固定长度的array<ResponsePacket, 8>并遍历8个元素,但实际收到的字节数可能不等于8 * sizeof(ResponsePacket)。这种越界访问会破坏内存中的其他变量(比如定时器、socket的内部状态),导致后续接收操作无法正常执行。

2. 重复发起异步接收操作

write函数末尾额外调用了read(),而初始的接收循环已经通过configure中的read()和handle_incoming的递归调用维持。重复发起的接收操作会导致socket上同时存在多个待处理的异步接收任务,引发逻辑混乱,干扰正常的接收流程。


修复代码

修复handle_incoming函数(解决内存越界)

auto PowerSwitch::handle_incoming(const usize bytes) -> void
{
    const size_t packet_size = sizeof(ResponsePacket);
    // 校验数据长度是否合法
    if (bytes % packet_size != 0) {
        llog::warn("powerswitch: 收到非法数据(长度{}不是{}的整数倍)", bytes, packet_size);
        return;
    }

    const size_t packet_count = bytes / packet_size;
    const auto raw = span(this->m_buffer).first(bytes);
    const auto packets = reinterpret_cast<const ResponsePacket*>(raw.data());
    
    for (size_t i = 0; i < packet_count; ++i) {
        const auto& pkt = packets[i];
        // 校验通道号合法性
        if (pkt.channel >= m_channels.size()) {
            llog::warn("powerswitch: 无效通道号{}", pkt.channel);
            continue;
        }
        this->m_channels[pkt.channel] = ChannelData {
            .voltage = static_cast<f32>(pkt.voltage) / 1000.0f,
            .current = static_cast<f32>(pkt.current),
            .enabled = static_cast<bool>(pkt.enabled)
        };
        llog::trace("[通道{}: {} {}V {} mA]", pkt.channel, m_channels[pkt.channel].enabled, 
                    m_channels[pkt.channel].voltage, m_channels[pkt.channel].current);
    }
}

修复write函数(移除多余的read调用)

auto PowerSwitch::write(const string_view data) -> void
{
    this->m_socket.async_send_to(
        asio::buffer(data),
        this->m_target,
        [this](const auto& ec, const auto& bytes_transferred)
        {
            if(ec) {
                llog::error("powerswitch: 发送错误: {}", ec.what());
                return;
            }
            llog::trace("powerswitch: 向{}:{}发送了{}字节",
                this->m_target.address().to_string(),
                this->m_target.port(),
                bytes_transferred
            );
        }
    );
    // 删除此行多余的read()调用
}

可选优化:增强接收错误处理

避免临时错误导致接收循环终止:

auto PowerSwitch::read() -> void
{
    this->m_socket.async_receive_from(
        asio::buffer(this->m_buffer),
        this->m_endpoint,
        [this](const auto& ec, const auto& bytes_transferred)
        {
            if(ec) {
                llog::error("powerswitch: 接收错误: {}", ec.what());
                // 非关闭类错误,尝试重启接收
                if (ec != asio::error::operation_aborted && ec != asio::error::bad_descriptor) {
                    this->read();
                }
                return;
            }
            this->handle_incoming(bytes_transferred);
            this->read();
        }
    );
}

内容的提问来源于stack exchange,提问作者whs31-

相关产品推荐
方舟 Agent Plan

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

最近更新时间:2026.06.28 03:29:50