【问题标题】:Boost UDP async_receive_from doesn't wait for timeout or receiveBoost UDP async_receive_from 不等待超时或接收
【发布时间】:2013-12-12 18:27:48
【问题描述】:

我尝试使用 async_receive_from 方法来获取数据。但是当我开始时,程序不会等待超时或正在读取某些内容。我知道数据是从端口(wireshark)传入的。这可能是什么原因。我尝试使用来自boost website 的不同示例。当我调试代码时,我总是得到错误代码 125 (operation_aborted)。 这是我的代码的相关部分:

UDPCommunication::UDPCommunication(){
    m_portReceive = 2041;
    m_byteArrayLen = 0;
    m_lengthReceive = 0;
    m_dataReceived = false;
}

void UDPCommunication::openSocketReceive(){
    try{
        m_socketReceive = shared_ptr<udp::socket>(
                new udp::socket(m_ioService,
                        udp::endpoint(udp::v4(), m_portReceive)));

        m_receiveTimeout = shared_ptr<boost::asio::deadline_timer>(
                new boost::asio::deadline_timer(m_ioService));

        m_receiveTimeout->async_wait(
                boost::bind(&UDPCommunication::udpReceiveTimeoutHandler, this,
                        boost::asio::placeholders::error, 0));

        m_dataReceived = false;

    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
    }
}

void UDPCommunication::udpReceiveWithTimeout(long time){    
    try{
        boost::system::error_code ec;
        m_receiveTimeout->expires_from_now(boost::posix_time::seconds(time));
        ec = boost::asio::error::would_block;
        m_lengthReceive = 0;
        m_socketReceive->async_receive_from(buffer(m_bufferReceive),
                m_endpointReceive,
                boost::bind(&UDPCommunication::udpReceiveTimeoutHandler, this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        do{
            m_ioService.run_one();
        }while(ec == boost::asio::error::would_block);

    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
    }   
}

bool UDPCommunication::udpReceiveTimeoutHandler(
        const boost::system::error_code& ec, size_t size){
    try{
        m_socketReceive->cancel();
        m_receiveTimeout->cancel();
        if(ec != boost::asio::error::would_block){

            m_dataReceived = false;
            return false;
        }else{
            m_lengthReceive = sizeof(m_bufferReceive);
            m_vectorBuffer.resize(m_lengthReceive);
            int i = 0;
            for(std::vector<unsigned char>::iterator it =
                    m_vectorBuffer.begin(); it != m_vectorBuffer.end(); ++it){
                *it = m_bufferReceive[i];
                ++i;
            }
            m_dataReceived = true;
            return true;
        }
    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
        return false;
    }
    return false;
}

bool UDPCommunication::dataReceived(){
    return m_dataReceived;
}

void main(){
    udpCommunication = shared_ptr<UDPCommunication>(new UDPCommunication());
    udpCommunication->openSocketReceive();
    udpCommunication->udpReceiveWithTimeout(5);
    bool dataReceived = udpCommunication->dataReceived();

    std::cout<< dataReceived << std::endl; //is data coming?
}

上面问题的解决方案。 此代码工作正常:

int m_portReceive;
int m_byteArrayLen;
io_service m_ioService;
udp::endpoint m_endpointReceive;
shared_ptr<udp::socket> m_socketReceive;
shared_ptr<boost::asio::deadline_timer> m_receiveTimeout;
std::vector<unsigned char> m_vectorBuffer;
unsigned char m_bufferReceive[128];
size_t m_lengthReceive;
bool m_dataReceived;
bool m_timeoutFired;
boost::mutex m_mutex;

UDPCommunication::UDPCommunication(){
    m_portReceive = 2041;
    m_byteArrayLen = 0;
    m_lengthReceive = 0;
    m_dataReceived = false;
    m_timeoutFired = false;
}

void UDPCommunication::openSocketReceive(){
    try{
        m_socketReceive = shared_ptr<udp::socket>(
                new udp::socket(m_ioService,
                        udp::endpoint(udp::v4(), m_portReceive)));

        m_receiveTimeout = shared_ptr<boost::asio::deadline_timer>(
                new boost::asio::deadline_timer(m_ioService));

        m_receiveTimeout->expires_at(boost::posix_time::pos_infin);
        UDPCommunication::udpTimeoutHandler();

        m_receiveTimeout->async_wait(
                boost::bind(&UDPCommunication::udpTimeoutHandler, this));

        m_dataReceived = false;

    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
    }
}

void UDPCommunication::udpReceiveWithTimeout(long time){

    try{
        boost::system::error_code ec;
        m_receiveTimeout->expires_from_now(
                boost::posix_time::time_duration(
                        boost::posix_time::seconds(time)));

        m_socketReceive->async_receive_from(buffer(m_bufferReceive),
                m_endpointReceive,
                boost::bind(&UDPCommunication::udpReceiveHandler, this,
                        boost::asio::placeholders::error,
                        boost::asio::placeholders::bytes_transferred));
        do{
            m_ioService.run_one();
        }while((m_lengthReceive == 0)
                && (m_timeoutFired == false));

    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
    }
}

void UDPCommunication::udpReceiveHandler(const boost::system::error_code& ec,
        size_t size){
    try{
        m_mutex.lock();
        if(m_timeoutFired == false){
            if(size > 0){
                m_socketReceive->cancel();
                m_receiveTimeout->cancel();
                m_lengthReceive = size;

                m_vectorBuffer.resize(m_lengthReceive);
                int i = 0;
                for(std::vector<unsigned char>::iterator it =
                        m_vectorBuffer.begin(); it != m_vectorBuffer.end();
                        ++it){
                    *it = m_bufferReceive[i];
                    ++i;
                }

                m_dataReceived = true;
            }else{
                m_lengthReceive = 0;
                m_socketReceive->async_receive_from(buffer(m_bufferReceive),
                        m_endpointReceive,
                        boost::bind(&UDPCommunication::udpReceiveHandler, this,
                                boost::asio::placeholders::error,
                                boost::asio::placeholders::bytes_transferred));
            }
        }
        m_mutex.unlock();
    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
        m_mutex.unlock();
    }
}


    void UDPCommunication::udpTimeoutHandler(){
    try{
        m_mutex.lock();
        if(m_lengthReceive == 0){
            if(m_receiveTimeout->expires_at()
                    <= deadline_timer::traits_type::now()){
                std::cout << "timeout: no data received from modem"
                        << std::endl;
                m_socketReceive->cancel();
                m_dataReceived = false;
                m_receiveTimeout->expires_at(boost::posix_time::pos_infin);
                m_timeoutFired = true;
            }
            m_receiveTimeout->async_wait(
                    boost::bind(&UDPCommunication::udpTimeoutHandler, this));
        }
        m_mutex.unlock();
    } catch(std::exception& e){
        std::cerr << e.what() << std::endl;
        ErrorLogging::log(e);
        m_mutex.unlock();
    }
}

bool UDPCommunication::dataReceived(){
    return m_dataReceived;
}

void main(){
    udpCommunication = shared_ptr<UDPCommunication>(new UDPCommunication());
    udpCommunication->openSocketReceive();
    udpCommunication->udpReceiveWithTimeout(5);
    bool dataReceived = udpCommunication->dataReceived();

    std::cout<< dataReceived << std::endl; //is data coming?

    //now do something with data...
}

【问题讨论】:

  • 程序是否完全终止?还是挂了?

标签: c++ asynchronous udp timeout boost-asio


【解决方案1】:

请注意,我没有检查每一行是否有其他错误,但我想到了两个问题:

  1. 您 async_wait 在您的 deadline_timer 上没有先设置到期时间。

  2. 您使用 io_service.run_one() 中间没有调用 io_service.reset()。

我不确定第一个做什么,但第二个意味着,一旦 run_one() 第一次返回(通过发布任何处理程序或通过停止蜜蜂),它会在每次再次调用时立即返回,没有做它的工作。

可能第一个使用 operation_aborted 调用您的超时处理程序,而第二点使您的 io_service 无法执行任何其他操作。

编辑: 还有一个问题

    do{
        m_ioService.run_one();
    }while(ec == boost::asio::error::would_block);

如果你想让 run_one() 方法改变 ec,你必须像这样传递它:run_one(ec) 原样,ec 是该方法中的局部变量,不应被任何东西修改,因此始终保持 will_block。当然,在这种情况下 run_one 将不再抛出任何东西,而是将其结果存储在 ec 中。

【讨论】:

  • 谢谢,这是我的问题之一。我将在起始帖中添加正确的代码。
  • 很高兴听到这个消息。如果可以的话,我建议使用两个不同的处理程序来接收和超时,以使处理更容易阅读。只需确保在取消另一个处理程序之前检查其中一个处理程序没有被 operation_aborted 调用(这意味着当前处理程序已被调用,因为相应的异步操作已被取消)。 UdpReceiveWithTimeout() 中的 ec 仍然什么都不做。你用一个值初始化它,没有办法改变那个值,然后检查这个值是否仍然相同。
  • 第一步是解决问题,现在我必须优化代码。非常感谢。
  • @Nik 我添加了我最新的解决方案。也许还有更多工作要做,但目前我对这段代码很好。
猜你喜欢
  • 1970-01-01
  • 2012-11-14
  • 1970-01-01
  • 2015-09-14
  • 2020-12-18
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
相关资源
最近更新 更多