2012-09-28 187 views
2

我想使用boost asio制作玩具异步回显客户端。由于某种原因,它在等待sent_ = true的第二个请求/回复周期中被阻塞,但是在接收到回声之前(没有服务器崩溃)。UDP Boost ASIO异步客户端挂起

/* 
UDP asynchronous clint using boost asio library 
*/ 
#include <cstdlib> 
#include <iostream> 
#include <algorithm> 

#include <boost/array.hpp> 
#include <boost/asio.hpp> 
#include <boost/lexical_cast.hpp> 
#include <boost/thread.hpp> 
#include <boost/bind.hpp> 
#include <boost/chrono/duration.hpp> 

#include "dbook/test/tools.hpp" 

using boost::asio::ip::udp; 

const size_t N_MESSAGES = 1024; 

class udp_client { 
public: 
    udp_client(const std::string& host,const std::string& service) 
    :io_service_(), 
    socket_(io_service_), 
    replied_(false), 
    sent_(false) 
    { 
    socket_.open(boost::asio::ip::udp::v4()); 
    udp::resolver resolver(io_service_); 
    udp::resolver::query query(udp::v4(), host, service); 
    endpoint_ = *resolver.resolve(query); 

    } 
    ~udp_client() { 
    socket_.close(); 
    } 

    bool sent() { 
    return sent_; 
    } 

    void send(const int r) { 
    replied_ = false; 
    sent_ = false; 

    memcpy(&send_buf_[0],&r,sizeof(int)); 
    std::cout << "prepare sending" << std::endl; 

    socket_.async_send_to(boost::asio::buffer(send_buf_), endpoint_, 
       boost::bind(&udp_client::handle_send, this, 
         boost::asio::placeholders::error, 
         boost::asio::placeholders::bytes_transferred)); 
    io_service_.run_one(); 

    std::cout << "after run_one" << std::endl; 

    } 

    bool replied() { 
    return replied_; 
    } 

    int reply() { 
    return *reinterpret_cast<int*>(&recv_buf_[0]); 
    } 

private: 
    void handle_send(const boost::system::error_code& error, 
      std::size_t size) 
    { 
    if (error) { 
     //missing error propagation to main thread 
     std::cerr << "ERROR: Client error while sending (error code = " << error << "): " << std::endl; 
     std::cerr << "ERROR: Recovering..." << std::endl; 

    } else { 
     sent_ = true; 
     std::cout << "sent" << std::endl; 
     socket_.async_receive_from(boost::asio::buffer(recv_buf_), endpoint_, 
       boost::bind(&udp_client::handle_receive, this, 
         boost::asio::placeholders::error, 
         boost::asio::placeholders::bytes_transferred)); 
     io_service_.run_one(); 


    } 
    } 

    void handle_receive(const boost::system::error_code& error, 
       std::size_t size) { 
    if (error) { 
     //missing error propagation to main thread 
     std::cerr << "ERROR: Client error while receiving (error code = " << error << ")" << std::endl; 
     std::cerr << "ERROR: Recovering..." << std::endl; 

    } else { 
     std::cout << "received" << std::endl; 

     replied_ = true; 
    } 
    } 


private: 
    boost::asio::io_service io_service_; 
    udp::socket socket_; 
    udp::endpoint endpoint_; 
    volatile bool replied_; 
    volatile bool sent_; 
    volatile int reply_; 
    boost::array<char, sizeof(int)> send_buf_; 
    boost::array<char, sizeof(int)> recv_buf_; 

}; 

int main(int argc, char* argv[]) { 

    if (argc != 3) { 
    std::cerr << "Usage: udp_echo_client <host> <port>" << std::endl; 
    return 1; 
    } 

    try { 
    udp_client c(argv[1],argv[2]); 

    for(size_t i=0; i != N_MESSAGES; ++i) { 
     int r = rand(); 
     c.send(r); 

     //here we could put a tiemeout 
     while (!c.sent()) { 
    boost::this_thread::sleep(boost::posix_time::milliseconds(10)); 
     } 

     //here we could put a tiemeout 
     while (!c.replied()) { 
    boost::this_thread::sleep(boost::posix_time::milliseconds(10)); 
     } 
     int resp = c.reply(); 

     std::cout << "sent= " << r << ", received= " << resp << std::endl; 
     assert(r == resp); 
    } 

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

    return 0; 
} 

我得到的日志是:

$ ./bin/udp_echo_client localhost 11111 
prepare sending 
sent 
received 
after run_one 
sent= 16807, received= 16807 
prepare sending 
after run_one 

我想这是我关于如何使用升压ASIO缺乏了解。所以,我将不胜感激,如果有人可以解释为什么该程序的行为就像是:)

+2

一般情况下,你需要不断地查询'io_service'(或者更多的appror,只需调用'io_service :: run'。在这种情况下,你需要在'io_service :: run_one'完成后调用'io_service :: reset'。 – Chad

+0

@Chad值得做这个nt是答案。 –

回答

4

促进我的意见,所建议的:

一般情况下,你需要不断地轮询io_service(以上approrpaitely,只需调用io_service::run在这种情况下,你需要在io_service::run_one()完成之后调用io_service::reset()