【问题标题】:boost::asio and async SSL stream: how to detect end of data/connection close?boost::asio 和异步 SSL 流:如何检测数据结束/连接关闭?
【发布时间】:2012-01-18 00:45:21
【问题描述】:

我正在尝试结交 asio 和 SSL 朋友。 一切顺利,但有一件事造成了不便:如何 检测对端是否关闭连接,并将其与情况区分开来 当对等方只是在发送数据时稍作休息,旨在继续 几秒钟后?

  • 提升 1.48
  • OpenSSL 1.0.0e
  • 使用 VS10 编译为 32 位代码
  • 在 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)。

所以,问题是:这是预期的行为,还是我配置错误?

客户端代码sn-p:

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),我们将得到“短读”:(。

如果我们使用代码 as-ai,输出将是这样的:

请求是:

GET / HTTP/1.0

Cookie:Nama-nama=Vala-vala

传输的字节数:1024

回复:HTTP/1.0 200 ok 内容类型:text/html

.....bla-bla-bla ....

正在阅读... 传输的字节数:1024

..... 呜呜呜…… .....bla-bla-bla ....

正在阅读... 传输的字节数:482

..... 呜呜呜……

阅读中...

传输的字节数:0

同时,如果我们改用 async_read_some 写代码,那是为了什么 普通套接字将返回 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-socket,我们将得到 0 作为传输的字节,然后是 short_read。

我知道如果是对等点,则无法检测断开连接,因为 例如,刚刚从网络中拔出。 但是如何检测明确的干净对等点断开连接时的情况 对等方只是暂时不发送数据,但可能会这样做 晚一点?

或者,我可能不明白什么?

WBR, 安德烈

一些附录: SSL/TLS 有通知其他方关闭连接的符号。 它 close_notify 警报。也可以关闭底层 TCP 套接字。

所以,基本上,我的问题是:为什么在相同的条件下(TCP 套接字已明确关闭)在 tcp::socket 的情况下我收到 EOF,而对于 boost::asio::ssl:: 则没有收到任何内容流。

是 bug 还是 asio 功能?

还有一个补充: 由于某些原因,如果 SSL 接收 close_notify 或底层 TCP 套接字已关闭,asio 也没有给我 EOF。

是的,我可以通过超时检测死连接。 但是如何检测正确关闭的 SSL 连接?通过接收 short_read?

【问题讨论】:

    标签: ssl boost-asio


    【解决方案1】:

    此处应出现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&amp;) 来看到。

    我相信 ssl 实现是在 boost 1.47 中重写的,因此早期版本可能具有不同的行为。

    【讨论】:

    • 我没有看到简短的阅读。我看到了:如果远程主机调用shutdown,那么本地主机的读取将使用eof 完成。如果两个主机都调用了shutdown,那么对shutdown 的调用将以eof 完成。这些是 ssl::stream 关​​闭,而不是 TCP/IP 关闭。我已经写了一个测试来证实这一点。
    【解决方案2】:

    您可能对这些讨论感兴趣:

    本质上,当远程方断开普通 TCP 套接字时,有时(甚至大部分时间)您会收到 EOF,这只是运气。在一般情况下你不能依赖它,因为它不可能区分非活动套接字和突然关闭而不写入它的套接字。

    您需要在应用程序协议级别定义一些分隔符,以了解何时停止读取。 在 HTTP 中,这可以通过结束标头(对于标头)的空白行、定义正文长度的 Content-Length 标头或在事先不知道正文长度时的 chunked transfer encoding 定界符来完成。

    【讨论】:

    • SSL/TLS 有通知对方关闭连接的符号。它 close_notify 警报。底层 TCP 套接字也被关闭。所以,基本上,我的问题是:为什么在相同的条件下(TCP 套接字被清楚地关闭)在 tcp::socket 的情况下我收到 EOF,并且没有收到任何关于 boost::asio::ssl::stream 的内容。是错误还是功能?
    • 当然,close_notify 用于干净的闭包,它不会帮助您检测坏的闭包。我对boost::asio::ssl::stream 了解不多,但我的猜测是您正在执行异步操作并且 SSL/TLS 关闭会导致异步操作出现问题(请参阅上面的“正确关闭 SSLSocket”链接)。它可能解释了稍微不同的行为。无论哪种方式,这都无关紧要。这不是需要修复的错误,因为它不是找出何时停止从流/套接字读取的正确方法。
    • 顺便说一下,我不确定您所说的“它 close_notify 警报。底层 TCP 套接字也正在关闭。”是什么意思,但 TLS 关闭警报没有并不意味着必须关闭底层 TCP 套接字。
    • 我正在研究 SSL 级别。因此,如果对等方发送 close_notify,我希望得到一个 EOF。当然,如果底层 TCP 套接字关闭,我希望得到一个 EOF。但事实并非如此!好的,可以通过超时检测到无效的 SSL 连接。但是正确完成/关闭呢? asio 没有给我任何东西,除了 short_read。从常识的角度来看,在这种情况下什么不适合。 :(
    • 由于您无法区分“死亡”或未发送任何内容的远程方,因此检测 TCP/TLS 的正确关闭通常没有用:知道何时停止读取取决于应用程序.层。据推测,asio 抛出了一个异常。在截断攻击的情况下(即 TCP 关闭 w/o TLS close_notify)。一旦您阅读了您要阅读的内容(在此处的 HTTP 层),异常的存在/不存在将表明存在问题。知道是否有适当的关闭几乎无关紧要。如果它发生在 HTTP(或其他应用程序)消息的中间,无论如何你都会遇到问题。
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2011-12-05
    • 2015-06-05
    • 2014-10-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多