Вот такая ситуация. Есть http-клиент, который соединяется с сервером и получает пакет данных. На весь сеанс работы отводится 5 секунд. Если клиент не успевает за 5 сек. получить данные, он разрывает соединение. Проблема в том, что (редко) в случае когда соединение разрывается из-за таймаута, хендлер для чтения данных (OnRead(const error_code& err)) вызывается когда сокет уже закрыт, а в err код ошибки не operation_aborted (как следует из документации по asio), а 0, то есть ошибки-то и нет. Причем, в буффере для чтения действительно ответ сервера.
В связи с этим 2 вопроса.
1) Правильно ли я понимаю, что если данные уже пришли и находятся в буффере, хендлер удаляется из очереди io_service и даже после закрытия сокета будет вызван без ошибки?
2) Если это верно (то есть close() не гарантирует вызов хендлера с кодом ошибки operation_aborted), то как лучше решить эту проблему? Или так нормально: if(!err && skt_.is_open()) ?
Заранее благодарен.
Код для пояснения:
//соединяемся с endpoint_. Таймер нужен чтобы разорвать соединение после 5 секунд простоя.
void Client::Start()
{
timer_.expires_from_now(posix_time::seconds(5));
timer_.async_wait(bind(&Client::OnTimeout, shared_from_this(), asio::placeholders::error));
skt_.async_connect(endpoint_, bind(&Client::OnConnected, shared_from_this(), asio::placeholders::error));
}
//если прошло 5 секунд, закрываем сокет
void Client::OnTimeout(const error_code& err)
{
if(!err)
{
error_code e;
skt_.shutdown(asio::ip::tcp::socket::shutdown_both, e);
assert(!err);
skt_.close(e);
assert(!err);
}
}
//Функция не имеет значения, т.к. в случае таймаута сюда не попадаем
void Stop()
{
timer_.cancel();
}
//Если соединение установлено, посылается запрос.
void Client::OnConnected(const error_code& err)
{
if(!err)
asio::async_write(skt_, request_, bind(&Client::OnSent, shared_from_this(), asio::placeholders::error));
else if(err != asio::error::operation_aborted)
Stop();
}
//Запрос успешно отправлен серверу.
void Client::OnSent(const error_code& err)
{
if(!err)
asio::async_read_until(skt_, reply_, "\r\n\r\n", bind(&Client::OnRead, shared_from_this(), asio::placeholders::error));
else if(err != asio::error::operation_aborted)
Stop();
}
//В этот момент, между функциями OnSent и OnRead сработал таймер и вызвал OnTimeout. Было выполнено skt_.close() и OnRead должен вызваться с err == boost::asio::error::operation_aborted
void Client::OnRead(const error_code& err)
{
if(!err)
assert(skt_.is_open()); //Вот тут иногда программа падает после разрыва соединения по таймауту
//...
}