2012-09-28 3 views
2

부스트 asio를 사용하여 장난감 비동기식 에코 클라이언트를 만들려고합니다. 어떤 이유로 send_ = true를 기다리지 만 에코를 받기 전에 (서버가 충돌하지 않는 경우) 두 번째 요청/응답 사이클에서 차단됩니다.UDP 부스트 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'를 계속 폴링해야합니다 (또는 더 간단하게'io_service :: run '을 호출하십시오.)이 특별한 경우, 'io_service :: run_one'이 완료된 후에'io_service :: reset'을 호출해야합니다. – Chad

+0

@Chad 대답은 아닙니다. –

답변

4

, 같이

는 일반적으로, 당신은 io_service (이상 approrpaitely, 단지 io_service::run 전화를 지속적으로 폴링 할 필요가 이 특별한 경우에는 io_service::run_one()이 완료된 후에 io_service::reset()으로 전화해야합니다.

관련 문제