2016-11-27 8 views
1

에서 나는 다음을 수행 ASIO와 클라이언트를 작성하려고에 대한 혼란 :부스트 :: ASIO :: 수율 위해 실행 같은 가닥

  1. 가 서버에 연결합니다.
  2. 의 데이터를 다시 읽으려고 시도합니다.이 서버에 연결되었습니다.

내가 아는 문제는 예상대로 작업이 순서대로 실행되지 않는 것입니다. 여기 코드는 다음과 같습니다

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname, 
              int port, 
              std::string const & name) { 
    using namespace std::literals; 
    boost::asio::spawn 
     (strand_, 
     [this, name, port, hostname](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace() << name << " connecting to " << hostname << ':' 
            << port; 
      Guinyote::Utils::connectWith 
       (*this, std::move(hostname), port, 
       std::move(name), yield); 

      i_->playerLog->info() << "Connected to server."; 
     }); 
    runthread_ = std::thread([&] { 
      try { 
       i_->playerLog->info() << "Starting..."; 
       this->service_.run(); 
      } 
      catch (std::exception & e) { 
       std::cout << e.what() << std::endl; 
      } 
     }); 
    return this->asyncReceiveMessage(); //This function spawns another coroutine through the same strand_ object. 
} 

기능 this->asyncReceiveMessage() 서버가 연결 한 후 다시 전송하는 메시지가 나타날 것으로 예상된다 :

std::future<NetMessage> Cliente::asyncReceiveMessage() { 
    namespace ba = boost::asio; 

    std::promise<NetMessage> prom; 
    std::future<NetMessage> message = prom.get_future(); 
    ba::spawn 
     (strand_, 
     [this, p = std::move(prom)](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace("waiting to read message from server socket..."); 
      boost::system::error_code ec{}; 
      boost::int64_t messageSize{}; 
      ba::async_read(
       socket_, 
       ba::buffer(&messageSize, sizeof(boost::int64_t)), 
       yield); 

      i_->playerLog->trace() << "Client: Received message of " 
            << messageSize << " bytes. Reading message..."; 

      std::vector<char> serverMessageData(messageSize); 
      ba::async_read 
       (socket_, 
       ba::buffer(serverMessageData), 
       yield); 

      using namespace boost::iostreams; 
      basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size()); 
      stream<basic_array_source<char>> stream(input_source); 
      boost::archive::binary_iarchive archive(stream); 
      Utils::MensajeRed msg; 

      archive >> msg; 
      i_->playerLog->trace() << "NetMessage correctly read."; 
      p.set_value(std::move(msg)); 

     }); 
    return message; 
} 

내 로그 파일에서 내가 클라이언트 측에서 다음 무엇입니까를 :

[clientLog] [info] Client: Starting... 
[clientLog] [trace] User1234 connecting to localhost:10004 
[clientLog] [trace] waiting to read message from server socket... 

하지만 해당 로그의 세 번째 줄은 [clientLog] [info] Connected to server. 후 올 것이라고 기대 그래서 내 기대는 다음과 같습니다

[clientLog] [info] Client: Starting... 
[clientLog] [trace] User1234 connecting to localhost:10004 
[clientLog] [info] Connected to server. 
[clientLog] [trace] waiting to read message from server socket... 

즉, 항상 전에 "... 서버 소켓에서 메시지를 읽을 대기"일해야 "서버에 연결".

무슨 일이 일어나는지 아는 사람이 있습니까? strand_은 실행 순서를 보장 할 것이라고 생각했지만 뭔가 오해했을 수도 있습니다. 내가 원하는 효과를 얻으려면 올바른 해결책은 무엇입니까?

+0

나는 이것이 똑같은 것을 본다. 그러나 나는 적절한 해결책을 보지 못했다. 실제로 "손으로 직접 처리"와 같은 것을 말합니다 : http://stackoverflow.com/questions/19946555/boostio-service-how-to-guarantee-handler-execution-sequence –

답변

1

여기서 문제는 작업이 순서대로 시작된다는 것입니다. 그러나 후속 boost::asio::spawn 연쇄 호출을 실행한다고해서 첫 번째 작업이 두 번째 작업보다 먼저 완료되는 것은 아닙니다.

첫 번째 작업이 두 번째 작업 이전에 시작되고 다른 작업은 수행되지 않습니다.

순서를 유지하기 위해 방금 두 개의 다른 코 루틴을 생성하는 대신 asyncConnectTo에 스폰 중에 호출되는 coroutine을 만들었습니다. 내가 처음 코 루틴은 초 전에 완료 확인이 방법 :

NetMessage Cliente::asyncReceiveMessageCoro(boost::asio::yield_context yield) { 
    namespace ba = boost::asio; 
    i_->playerLog->trace("waiting to read message from server socket..."); 
    boost::system::error_code ec{}; 
    boost::int64_t messageSize{}; 
    ba::async_read(
     socket_, 
     ba::buffer(&messageSize, sizeof(boost::int64_t)), 
     yield); 

    i_->playerLog->trace() << "Client: Received message of " 
            << messageSize << " bytes. Reading message..."; 

    std::vector<char> serverMessageData(messageSize); 
    ba::async_read 
     (socket_, 
     ba::buffer(serverMessageData), 
     yield); 

    using namespace boost::iostreams; 
    basic_array_source<char> input_source(&serverMessageData[0], serverMessageData.size()); 
    stream<basic_array_source<char>> stream(input_source); 
    boost::archive::binary_iarchive archive(stream); 
    Utils::MensajeRed msg; 

    archive >> msg; 
    i_->playerLog->trace() << "NetMessage correctly read."; 
    return msg; 
} 

이 코 루틴은 마지막에 체인 수 있습니다

std::future<NetMessage> Cliente::asyncConnectTo(std::string const & hostname, 
              int port, 
              std::string const & name) { 
    using namespace std::literals; 

    std::promise<NetMessage> msgProm; 
    auto msg = msgProm.get_future(); 
    boost::asio::spawn 
     (strand_, 
     [this, name, port, hostname, p = std::move(msgProm)](boost::asio::yield_context yield) mutable { 
      i_->playerLog->trace() << name << " connecting to " << hostname << ':' 
            << port; 
      Guinyote::Utils::connectWith 
       (*this, std::move(hostname), port, 
       std::move(name), yield); 

      i_->playerLog->info() << "Connected to server."; 
      p.set_value(this->asyncReceiveCoro(yield)); 
     }); 
    runthread_ = std::thread([&] { 
      try { 
       i_->playerLog->info() << "Starting..."; 
       this->service_.run(); 
      } 
      catch (std::exception & e) { 
       std::cout << e.what() << std::endl; 
      } 
     }); 
    return msg; 
} 

내 오래된 asyncReceiveMessage 그냥 asyncReceiveMessageCoro를 호출 + spawn의 조합이된다.

+1

결과를 공유해 주셔서 감사합니다. – sehe