2013-06-25 89 views
0

我已經實現了一個非常標準的阻塞api的超時boost :: asio模擬仿真。這是我的主要週期:deadline_timer只等待一次

io_service io_svc; 
tcp::endpoint endpoint(tcp::v4(), m_port); 
tcp::acceptor acceptor(io_svc, endpoint); 

accept_helper acc_hlpr(acceptor, 5000); 

while (m_bStop == false) 
{ 
    tcp::socket socket(io_svc); 

    if (acc_hlpr.accept(socket)) 
    { 
    // do stuff 

    socket.close(); 
    } 
} 


這是輔助類

class accept_helper 
{ 
public: 
    accept_helper (tcp::acceptor &acc, size_t msTO) : m_timer(acc.get_io_service()), m_acceptor(acc), m_msTO(msTO) { } 

    bool accept (tcp::socket &socket) 
    { 
    m_bTimeout = false; 
    m_bAccept = false; 

    m_timer.expires_from_now(boost::posix_time::milliseconds(m_msTO)); 
    m_timer.async_wait(boost::bind(&accept_helper::handle_timeout, this, boost::asio::placeholders::error)); 

    m_acceptor.async_accept(socket, boost::bind(&accept_helper::handle_accept, this)); 

    m_timer.get_io_service().run_one(); 
    m_timer.get_io_service().reset(); 

    if (m_bAccept) 
    { 
     m_timer.cancel(); 
     return true; 
    } 
    else if (m_bTimeout) 
    { 
     // BOOST_ASIO_ENABLE_CANCELIO is defined 
     boost::system::error_code ec; 
     m_acceptor.cancel(ec); 
    } 

    return false; 
    } 

private: 
    void   handle_accept (void) 
    { 
    boost::mutex::scoped_lock lock(m_mutex); 
    m_bAccept = true; 
    } 

    void   handle_timeout(const boost::system::error_code & error) 
    { 
    if (!error) 
    { 
     boost::mutex::scoped_lock lock(m_mutex); 
     m_bTimeout = true; 
    } 
    } 

private: 
    boost::asio::deadline_timer m_timer; 
    boost::asio::ip::tcp::acceptor &m_acceptor; 
    boost::mutex m_mutex; 
    size_t m_msTO; 
    bool m_bTimeout; 
    bool m_bAccept; 
}; 

的問題是等待計時器只在第一次循環。在其他run_one方法立即返回並沒有設置標誌。我試圖讓本地計時器,但沒有幫助。如何讓計時器每次都等待?

固定版本

bool accept (tcp::socket &socket) 
    { 
    m_bTimeout = false; 
    m_bAccept = false; 

    m_timer.expires_from_now(boost::posix_time::milliseconds(m_msTO)); 
    m_timer.async_wait(boost::bind(&accept_helper::handle_timeout, this, boost::asio::placeholders::error)); 

    m_acceptor.async_accept(socket, boost::bind(&accept_helper::handle_accept, this, boost::asio::placeholders::error));  

    m_timer.get_io_service().reset(); 
    m_timer.get_io_service().run_one(); 

    if (m_bAccept) 
    { 
     m_timer.cancel(); 
    } 
    else if (m_bTimeout) 
    { 
     boost::system::error_code ec; 
     m_acceptor.cancel(ec); 
    } 

    while (m_timer.get_io_service().run_one()); 

    return m_bAccept; 
    } 

private: 
    void   handle_accept (const boost::system::error_code & error) 
    { 
    if (!error) 
    { 
     boost::mutex::scoped_lock lock(m_mutex); 
     m_bAccept = true; 
    } 
    } 

    void   handle_timeout(const boost::system::error_code & error) 
    { 
    if (!error) 
    { 
     boost::mutex::scoped_lock lock(m_mutex); 
     m_bTimeout = true; 
    } 
    } 

回答

2

io_service::reset()功能只允許io_service恢復從停止狀態運行;它不會刪除已經排隊進入io_service的任何處理程序。在這種情況下,在io_serviceasync_waitasync_accept)上啓動了兩個操作,但由於io_service的事件循環正在由io_service::run_one()進行處理,因此只執行一個處理程序。在下一次調用accept_helper::accept()時,將執行前一個調用的處理程序。

要解決此問題,請考慮運行io_service,直到調用兩個處理程序。一些解決方案將運行io_service完成,如answer和Boost.Asio blocking tcp client timeout example所示。

+0

它現在的作品,謝謝。我已經更新了固定版本的帖子,可以嗎,像這樣使用它? – Sergi0

+1

@Sergi0:Yup,只要'io_service'只用於這兩個操作,看起來很好。否則,你會想要在完成處理程序中更新狀態,並檢查while循環體內的狀態以確定處理程序是否已被執行。 –