Чтение из закрытого сокета в boost.asio
От: Wody  
Дата: 26.07.11 22:35
Оценка:
Вот такая ситуация. Есть 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()); //Вот тут иногда программа падает после разрыва соединения по таймауту
 //...
}
 
Подождите ...
Wait...
Пока на собственное сообщение не было ответов, его можно удалить.