2013-04-17 4 views
1

저는 오랜 기간 동안 다른 서버와 통신하기 위해 몇 가지 영구적 인 기능을 사용할 수 있어야하는 프로젝트를 진행하고 있습니다. 이 서버는 상당히 높은 처리량을 갖습니다. 영구 연결을 올바르게 설정하는 방법을 찾는 데 문제가 있습니다. 이 작업을 수행하는 가장 좋은 방법은 영구 연결 클래스를 만드는 것입니다. 이상적으로는 한 번 서버에 연결하고 async_writes를 수행하면 정보가 나에게 들어옵니다. 그리고 그것이 나에게 돌아올 때 정보를 읽으십시오. 나는 내가 올바르게 나의 클래스를 형성하고있다라고 생각하지 않는다. 여기영구적 인 ASIO 연결

내가 지금 구축 한 것입니다 :

persistent_connection::persistent_connection(std::string ip, std::string port): 
    io_service_(), socket_(io_service_), strand_(io_service_), is_setup_(false), outbox_() 
{ 
    boost::asio::ip::tcp::resolver resolver(io_service_); 
    boost::asio::ip::tcp::resolver::query query(ip,port); 
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query); 
    boost::asio::ip::tcp::endpoint endpoint = *iterator; 
    socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator)); 
    io_service_.poll(); 
} 

void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator) 
{ 
    if(ec) 
    { 
     std::cout << "Couldn't connect" << ec << std::endl; 
     return; 
    } 
    else 
    { 
     boost::asio::socket_base::keep_alive option(true); 
     socket_.set_option(option); 
     boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n", boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error)); 
    } 
} 

void persistent_connection::write(const std::string &message) 
{ 
    write_impl(message); 
    //strand_.post(boost::bind(&persistent_connection::write_impl, this, message)); 
} 

void persistent_connection::write_impl(const std::string &message) 
{ 
    outbox_.push_back(message); 
    if(outbox_.size() > 1) 
    { 
     return; 
    } 
    this->write_to_socket(); 
} 

void persistent_connection::write_to_socket() 
{ 
    std::string message = "GET /"+ outbox_[0] +" HTTP/1.0\r\n"; 
    message += "Host: 10.1.10.120\r\n"; 
    message += "Accept: */*\r\n"; 
    boost::asio::async_write(socket_, boost::asio::buffer(message.c_str(), message.size()), strand_.wrap(
          boost::bind(&persistent_connection::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred))); 

} 

void persistent_connection::handle_write(const boost::system::error_code& ec, std::size_t bytes_transfered) 
{ 
    outbox_.pop_front(); 
    if(ec) 
    { 
     std::cout << "Send error" << boost::system::system_error(ec).what() << std::endl; 
    } 
    if(!outbox_.empty()) 
    { 
     this->write_to_socket(); 
    } 
    boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n",boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error)); 
} 

이 잘 보낼 것 같다에서 내가 보내드립니다 첫 번째 메시지는 서버가 그것을 가져오고 유효한 응답으로 응답합니다. 불행히도 두 가지 문제가 나타납니다.

1) async_write 명령을 실행 한 후 내 handle_write가 호출되지 않습니다. 이유가 없습니다. 2)이 프로그램은 응답을 읽지 않습니다. asyn_read_until은 해당 함수가 발생할 때까지 호출되지 않기 때문에 # 1과 관련이 있다고 생각합니다. 3) 누군가 내가 내 strand_.post 콜을 주석 처리 한 이유가 무엇인지 알 수 있는지 궁금합니다.

나는 대부분이 내 io_service를 어떻게 사용해야하는지에 대한 지식이 부족하기 때문에 내 생각에 누군가가 내게 크게 감사할만한 포인터를 줄 수 있다면 말이다. 추가 정보가 필요하면 좀 더 제공하게되어 기쁩니다.

int main() 
{ 
    persistent_connection p("10.1.10.220", "80"); 
    p.write("100"); 
    p.write("200"); 
    barrier b(1,30000); //Timed mutex, waits for 300 seconds. 
    b.wait(); 
} 

void persistent_connection::handle_read_headers(const boost::system::error_code &ec) 
{ 
    std::istream is(&buf_); 
    std::string read_stuff; 
    std::getline(is,read_stuff); 
    std::cout << read_stuff << std::endl; 
} 
+0

게시 한 코드에서 결코 write를 호출하지 않으며 읽기 핸들러가 없습니다. 그걸 제공 할 수 있니? – Loghorn

+0

당신은'io_service'를 다중 스레드에서'run'을 호출하고 있습니까? 왜냐하면 그렇지 않다면 가닥이 필요하지 않기 때문입니다. 여러 스레드에서 호출하는 경우 * 동일한 스레드에 대해 모든 처리기를 래핑해야합니다. 그렇지 않으면 동일한 연결의 처리기가 동시에 실행될 수 있습니다. –

+0

여러 스레드에서 쓰기를 호출합니다. 이 프로그램의 구조는 asio 웹 서버입니다. 각 요청에 대해 정보를 구문 분석 한 다음 구문 분석 된 정보를 영구 연결로 보냅니다. 그래서 각각의 연결은 write를 호출 할 것이고, 각각의 연결은 다른 쓰레드에있게 될 것입니다. – Eumcoz

답변

3

설명 동작은 더 이상 처리되는 io_service_의 이벤트 루프의 결과입니다

가 작성하는 당신에게

편집 호출을 감사드립니다.

생성자는 실행할 준비가 된 처리기를 실행하고 작업 완료를 기다리지 않는 처리기를 실행하며 io_service::run()은 모든 작업이 완료 될 때까지 차단합니다. 따라서 폴링 할 때 연결의 다른 쪽에서 데이터를 쓰지 않으면 아무 핸들러도 실행할 준비가되지 않고 실행은 poll()에서 반환됩니다.

스레딩과 관련하여 각 연결에 자체 스레드가 있고 통신이 HTTP와 같은 반이중 프로토콜 인 경우 동 기적으로 쓰면 응용 프로그램 코드가 더 간단 할 수 있습니다. 반면에 각 연결에 자체 스레드가 있지만 코드가 비동기 적으로 작성되면 이벤트 루프 내에서 예외가 처리되는 것을 고려하십시오. Boost.Asio의 effect of exceptions thrown from handlers을 읽는 것이 좋습니다.

또한 persistent_connection::write_to_socket()에는 정의되지 않은 동작이 도입되었습니다. boost::asio::async_write()을 호출 할 때 호출자가 버퍼의 소유권을 보유하고 핸들러가 호출 될 때까지 버퍼가 유효하게 유지되어야한다는 것을 보장해야합니다. 이 경우 message 버퍼는 자동 변수이며 그 수명은 persistent_connection::handle_write 처리기가 호출되기 전에 끝날 수 있습니다. 한 가지 해결책은 message의 수명을 persistent_connection과 일치하도록 멤버 변수로 변경하는 것입니다.

+0

이 동기식 쓰기 문제는 서버가 연결 중일 때 정보를 반환하는 데 최대 100ms가 걸릴 수 있다는 것입니다. 따라서 예제에서 두 개의 메시지를 보내면 메시지 2가 메시지 1보다 먼저 완료 될 수 있습니다. 수천 개의 연결을 초당 처리 할 수 ​​있도록 서버를 구축해야합니다. 이 점을 고려하여 단일 연결을 통해 여러 요청을 동시에 보내고 정보를 수신하고 ID를 가진 사람을 보낸 사람과 일치하는 것이 실용적이라고 생각했습니다. 이게 내가 아시오 소켓을 사용해야하는 방법이 아닌가? – Eumcoz

+0

각 연결마다 고유 한 스레드가 있고 반이중 프로토콜을 사용하는 경우 비동기 프로그래밍에서 얻은 정보는 거의 없습니다. 코드가 HTTP를 통해 전이중 통신을 시도하고있는 것처럼 보입니다. HTTP는 자체적 인 복잡성이 있습니다. 초당 수천 개의 연결을 처리 할 계획이라면 전이중 통신을 지원하는 프로토콜과 스레드 풀을 사용하는 것이 좋습니다. –

+0

좋아, 네가 지금 무슨 말하는지 이해할 것 같아. 나는 이것이 잘못된 길로 가고 있다고 생각한다. 데이터를 "스트리밍"하고자하는 몇 가지 지속적인 연결 대신, 모든 요청을 한 번에 하나씩 아웃 바운드로 처리하고 이들 모두를 동기식으로 처리하기에 충분한 지속적인 연결을 만드는 것이 더 낫습니다. 따라서 100ms마다 10 건의 요청이 발생하고 (응답 시간이 100ms 가량 지연되는 경우), 트래픽을 처리하기 위해 10 개의 영구 연결이 필요하며 계속 재사용해야합니다. – Eumcoz