2015-10-27 2 views
3

Я использую boost.asio, и у меня есть простой сервер, которому необходимо обрабатывать несколько соединений. Прежде чем начать «подключенный» статус, мне нужно выполнить рукопожатие с клиентом в течение ограниченного периода времени. Я использую прикованные асинхронные операции для каждого шага рукопожатия, поскольку мне нужно использовать таймер и (насколько я знаю, я не могу этого сделать с синхронными чтениями и записью). Мне нужен способ заблокировать каждое соединение до тех пор, пока таймер тайм-тайма не закончится или рукопожатие не удастся, без блокировки сервера.asio - Дождитесь завершения операций async

Есть ли способ достичь этого?

UPDATE Некоторый код для уточнения вещи

Простой сервер

typedef boost::asio::ip::tcp::socket sock; 

class server 
{ 
public: 
    server() : _ios(), _acceptor(_ios) 
    { 
     boost::asio::ip::tcp::resolver resolver(_ios); 
     boost::asio::ip::tcp::endpoint endpoint(boost::asio::ip::tcp::v4(), PORT); 

     _acceptor.open(endpoint.protocol()); 
     _acceptor.set_option(boost::asio::ip::tcp::acceptor::reuse_address(true)); 
     _acceptor.bind(endpoint); 
     _acceptor.listen(); 

     do_accept(); 
    } 

    void run() 
    { 
     _ios.run(); 
    } 

private: 
    void do_accept() 
    { 
     auto socket = std::make_shared<TSock>(_ios); 
     _acceptor.async_accept(*socket, std::bind(&server::handle_accept, 
                this, socket, std::placeholders::_1)); 
    } 

    void handle_accept(std::shared_ptr<sock> socket, const boost::system::error_code& ec) 
    { 
     auto connection = std::make_shared<CCon>(); 

     if (connection->Accept(socket)) 
     { 
      std::cout << "Connection accepted" << std::endl; 
     } 
     else 
     { 
      std::cout << "Connection not accepted" << std::endl; 
     } 

     do_accept(); 
    } 

    boost::asio::io_service   _ios; 
    boost::asio::ip::tcp::acceptor _acceptor; 

    std::set<std::shared_ptr<CCon>> _connections; 
}; 

int32_t main(int32_t argc, char *argv[]) 
{ 
    server s; 
    s.run(); 
} 

connection-> Accept (гнездо) объяснил

bool CCon::Accept(<std::shared_ptr<sock>> tcpSocket) 
{ 
    // set handshake sequence 
    SendGreeting(); 

    // I NEED TO WAIT HERE UNTIL connectionAccepted gets a value 

    if (connectionAccepted) 
    { 
     // Connection Accepted 
     return(true) 
    } 
    else 
    { 
     //Connection Rejected 
     return(false) 
    } 
} 

SendGreeting() содержит

boost::asio::async_write(*tcpSocket, 
         boost::asio::buffer(oBuff,bytesBuffered), 
         std::bind(&CCon::WaitForResp, 
            this, std::placeholders::_1)); 

Проблема в том, что WaitForResp никогда не вызывается. Он вызывается только после перезапуска io_service (stop() ->reset() ->run()) после установки нового обработчика, который не является решением.

Я думаю, что у меня что-то не хватает об Асио, и я был бы рад, если бы кто-нибудь мог мне помочь.

+0

Мне нужно использовать асинхронные функции, потому что мне нужен таймер для рукопожатия, но мне также нужно подождать (заблокировать) часть моего кода для завершения рукопожатия или таймера до конца. – Bituki

+1

Я пробовал использовать фьючерсы, но каким-то образом рукопожатие застревает в первом обработчике, пока 'future' не закончит свое время ожидания, а затем все рукопожатие (возможно, мне нужно, чтобы io_service работал в другом потоке?). Мне нужно некоторое время, чтобы упростить мой код и опубликовать его здесь, но я сделаю это, чтобы было ясно. – Bituki

+0

@TechnikEmpire Я добавил код. Надеюсь, теперь все ясно. – Bituki

ответ

1

Ваш примерный код не является полным, поэтому я не могу гарантировать, что мой ответ - это решение, но я укажу несколько вещей, которые, по моему мнению, являются проблемой.

Пункт 1 - io_service не просто продолжает работать. Если он думает, что больше нечего делать (ничего не стоит), то ::run() выйдет, и, поскольку вы ожидаете этого, вы не получите неожиданного поведения. Вам нужно предотвратить его работу, создав и сохранив объект io_service::work. Из документов:

boost::asio::io_service io_service; 
boost::asio::io_service::work work(io_service); 
... 

Подробнее об этом here.

Во-вторых, меня беспокоит то, как поддерживается срок службы объекта CCon. Кто несет ответственность за его сохранение? Я спрашиваю, потому что вы привязаны к внутри него, поэтому вы вскрываете асинхронные обратные вызовы внутри, но, видимо, полагаетесь на внешний объект, чтобы убедиться, что this жив, если и когда возвращается обратный вызов. Если вы не можете сделать это правильно, вы получите неопределенное поведение, что не обязательно означает, что вы получите большой код сбоя и ошибки, но, возможно, каким-то образом обработчик завершает или вешает навсегда, или какое-то другое странное неубедительное поведение из-за неопределенного состояния.

Исходя из неполного кода здесь, похоже, что ничего не держит CCon живым извне, а это значит, что вещи, вероятно, выпадают из сферы действия и уничтожаются. Я собираюсь предположить, что вы всегда получаете «соединение не принято» на вашем сервере, если вообще что-либо.Если вы нарушите код, который у вас есть, вы создадите CCon, завернутый в shared_ptr, затем позвоните ::Accept(), который, в свою очередь, вызывает , не блокируя методы async, которые могут быть выполнены немедленно или через 100 лет. Так как они неблокирующие, код будет немедленно действовать в операторе if(connectionAccepted) и, следовательно, немедленно вернуться в одну сторону, после чего ваш shared_ptr из CCon выйдет из сферы действия на сервере handle_accept, счетчик ссылок уменьшится до 0 и вызывается деструктор.

То, что вы должны делать это делают CCon наследовать от std::enable_shared_from_this или boost::enable_shared_from_this и во всех асинхронных обратных вызовах, которые CCon связывают внутри, вы должны связываться с shared_from_this(), который будет кормить другой shared_ptr в реплику от привязки, увеличивающийся реф счет вашего общего объекта CCon и гарантирующий срок службы CCon, чтобы, по крайней мере, распространиться на обработчик завершения.

В-третьих, я думаю, что ваш подход к дизайну - не лучшее решение, как примечание. Вы не должны заставлять все другие возможные входящие соединения ждать, пока предыдущее соединение не пройдет через какой-либо процесс аутентификации. Вы удерживаете акцептор от выполнения своей работы для новых клиентов до тех пор, пока предыдущий клиент не отправится на переговоры с сервером. Это совершенно ненужное узкое место.

Я считаю, что вы это делаете, потому что хотите на уровне объектов ::server определить, правильно ли клиенты прошли проверку подлинности. Вместо этого вы должны просто передать функцию или что-то в метод CCon::Accept(...), который он может вызывать для отправки сообщений на сервер, если вам действительно нужен сервер, чтобы что-то знать вообще. Скопируйте модель asio и измените свой метод Accept на AsyncAccept, сделайте его неблокирующим и дайте ему обратный вызов, когда он будет завершен, и в это время вы сможете проверить его окончательное состояние.

+0

Спасибо за ваш ответ, но: 1 '' работа здесь может быть не нужна здесь, поскольку я вызываю 'do_accept()' в конце 'handle_accept()', поэтому 'io_service' всегда что-то делать, например [здесь] (http://www.boost.org/doc/libs/1_59_0/doc/html/boost_asio/example/cpp11/http/server/server.cpp). 2 - Спасибо за ваш совет, вы совершенно правы в этом, но после изменения этого поведения сервер все тот же ..., это не причина, которая вызывает у меня проблемы. – Bituki

+0

@Bituki Вы видели часть, связанную с правильной привязкой к 'shared_from_this' внутри' CCon'? Поскольку из кода, который вы опубликовали, ваш 'shared_ptr'' CCon' скоро выпадет из области действия, и деструктор будет вызван. Печать «уничтожена» на консоли в '~ CCon' для проверки. –

+0

Да, я сказал в своем втором пункте, что я применил ваши советы, но это не решило мою проблему. – Bituki

Смежные вопросы