boost :: asio и асинхронный поток SSL: как определить конец данных / закрытие соединения? - PullRequest
16 голосов
/ 12 декабря 2011

Я пытаюсь подружиться с asio и SSL.Все идет хорошо, но одно доставляет неудобства: как определить, закрыто ли одноранговое соединение, и отличить его от ситуации, когда одноранговый узел просто делает короткий перерыв при отправке данных, чтобы продолжить его через несколько секунд?

  • boost 1.48
  • OpenSSL 1.0.0e
  • Скомпилирован в 32-битный код с использованием VS10
  • Работает на W7 x64.

Моя путаницаПроисходит от того, что поведение asio отличается для обычных сокетов и SSL-потоков.Если я использую tcp :: socket - я получаю ошибку EOF при закрытии соединения.Но для boost :: asio :: ssl :: stream - дело не в этом.Вместо этого async_read_some возвращает 0 при передаче байтов, а если я пытаюсь продолжить чтение из потока SSL - возвращает short_error (http://www.boost.org/doc/libs/1_47_0/doc/html/boost_asio/overview/core/streams.html).

Итак, возникает вопрос: ожидается ли это поведение илиЯ что-то неправильно настроил?

Фрагмент кода клиента:

class client
{
public:

    // bla-bla-bla-bla-bla ....
    //
   void handle_write(const boost::system::error_code& error)
   {
       if (!error)
       {
           socket_.async_read_some(boost::asio::buffer(reply_, max_length),
               boost::bind(&client::handle_read, this,
               boost::asio::placeholders::error,
               boost::asio::placeholders::bytes_transferred));
       }
       else
       {
           std::cout << "Write failed: " << error.message() << "\n";
       }
   }

   void handle_read(const boost::system::error_code& error,
                   size_t bytes_transferred)
   {

       std::cout << "Bytes transfered: " << bytes_transferred << "\n";
       if (!error)
       {
           std::cout << "Reply: ";
           std::cout.write(reply_, bytes_transferred);
           std::cout << "\n";

           std::cout << "Reading...\n";
           socket_.async_read_some(boost::asio::buffer(reply_, max_length),
               boost::bind(&client::handle_read, this,
               boost::asio::placeholders::error,
               boost::asio::placeholders::bytes_transferred));
       }
       else if (0 != bytes_transferred)
       {
           std::cout << "Read failed: " << error.message() << ":" 
                     << error.value() <<  "\n";
       }
   }

private:
   boost::asio::ssl::stream<boost::asio::ip::tcp::socket> socket_;
   boost::asio::streambuf request_;
   char reply_[max_length];
};

Если мы удалим if (0! = Bytes_transferred), мы получим "краткое чтение": (.

Если мы будем использовать код как-ai, вывод будет выглядеть примерно так:

Запрос:

GET / HTTP / 1.0

Cookie: Nama-nama = Vala-vala

Передано байт: 1024

Ответ: HTTP / 1.0 200 ok Тип контента: text / html

..... bla-bla-бла ....

Чтение ... Передано байтов: 1024

..... бла-бла-бла .... ..... бла-бла-бла....

Чтение ... Передано байтов: 482

..... бла-бла-бла ....

Чтение ...

Передано байтов: 0

В то же время, если вместо async_read_some мы напишем код, что за обычный сокет wплохо вернет EOF:

boost::asio::async_read(socket_, response_,
    boost::asio::transfer_at_least(1),
    boost::bind(&client::handle_read_content, this,
    boost::asio::placeholders::error));

тогда для SSL-сокета мы получим 0 в качестве переданных байтов, а затем short_read.

Я знаю, что нет способа обнаружить разрыв соединения в случае, еслиpeer, например, был просто отключен от сети.Но как обнаружить явное чистое отключение однорангового узла от ситуации, когда одноранговый узел просто не отправляет данные в течение некоторого времени, но, может быть, сделает это немного позже?

Или, может быть, я что-то не понимаю?

WBR, Андрей

Некоторое addentum: SSL / TLS имеет примечание, чтобы сообщить другой стороне о закрытии соединения.Это оповещение close_notify.Кроме того, базовый сокет TCP может быть закрыт.

Итак, в основном мой вопрос таков: почему в тех же условиях (сокет TCP был закрыт четко) я получаю EOF в случае tcp :: socket и не получаючто-нибудь для boost :: asio :: ssl :: stream.

Это ошибка или функция asio?

Еще одно добавление: по некоторым причинам asio не дал мне EOF, даже если SSLполучить close_notify, ни нижележащий сокет TCP не был закрыт.

Да, я могу обнаружить потерянные соединения по таймауту.Но как определить правильно закрытые SSL-соединения?Получая short_read?

Ответы [ 2 ]

14 голосов
/ 02 августа 2013

Здесь ожидается ошибка SSL_R_SHORT_READ.Когда сервер инициирует чистое отключение с помощью SSL_Shutdown, это отправляет клиенту предупреждение о закрытии уведомления о закрытии.Реализация Asio отображает это в ошибку SSL_R_SHORT_READ с категорией error::get_ssl_category().Он делает это, обнаруживая, инициировал ли одноранговое отключение через SSL_get_shutdown.

Это можно увидеть, проверив заголовок asio/ssl/detail/impl/engine.ipp и, в частности, функцию engine::map_error_code(boost::system::error_code&).

Я считаю, что реализация ssl была переписана в boost 1.47, поэтому более ранние версии имеют потенциально другое поведение.

6 голосов
/ 12 декабря 2011

Вас могут заинтересовать эти обсуждения:

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

Вам нужно определить разделители на уровне протокола приложения, чтобы знать, когда прекратить чтение. В HTTP это делается либо через пустую строку, которая оканчивает заголовок (для заголовка), заголовок Content-Length, определяющий длину тела, либо через разделитель кодирования chunked , ограничивающий длину тела, когда нет ' заранее известно.

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...