2011-12-11 161 views
15

我正在尝试使asio和SSL的朋友。 一切进展顺利,但有一件事情造成不便:如何在 检测到对端关闭连接,并且当对端在发送数据时只是短暂休息时,将其与 区分开来,几秒后继续 ?boost :: asio和async SSL流:如何检测数据结束/连接关闭?

  • 升压1.48
  • OpenSSL的1.0.0e
  • 使用VS10
  • 上W7 64工作编译为32位代码。

我的困惑来自这样一个事实,即对于 普通套接字和SSL流,asio行为不同。 如果我使用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]; 
}; 

如果我们去除如果(!0 = bytes_transferred),我们会得到 “短读” :(

如果我们将使用代码为爱,输出将是这样的:

请求是:

GET/HTTP/1.0

的Cookie:那抹-NAMA =瓦拉-VALA

字节转移:1024

答复:HTTP/1.0 200 OK 内容类型:文本/ HTML

..... BLA -bla-BLA ....

阅读... 字节转移:1024

.....喇嘛喇嘛喇嘛.... .....喇嘛喇嘛,喇嘛....

阅读... 字节转移: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插座,我们将得到0作为字节传输,然后short_read。

我知道在没有办法检测断开连接的情况下,如果对方(例如 示例)刚刚从网络中拔出。 但是如何检测明确的干净的同行脱节当 对端只是不发送数据一段时间,但可能会做到这一点 稍后?

或者,可能是我不明白的东西?

WBR, 安德烈

一些addentum: SSL/TLS有符号告知对方关于关闭连接。 它close_notify警报。底层TCP套接字也可以关闭。因此,基本上,我的问题是:为什么在相同的条件下(TCP套接字已清楚地关闭),我在tcp :: socket的情况下收到EOF,并且没有收到任何用于boost :: asio :: ssl的东西: :流。

它是错误还是asio功能?

又一个原因: 由于某些原因,如果SSL收到close_notify或底层TCP套接字已关闭,asio不会给我一个EOF。

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

回答

5

你可能有兴趣在这些讨论中:

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

您需要在应用程序协议级别定义一些分隔符以知道何时停止阅读。 在HTTP中,这可以通过结束标题的空行(用于标题),定义主体长度的标头Content-Length或者当身体长度事先不知道时用chunked transfer encoding定界符完成。

+1

SSL/TLS有通知通知对方关闭连接。 它close_notify警报。同时底层的TCP套接字也被关闭。因此,基本上,我的问题是:为什么在相同的条件下(TCP套接字已清楚地关闭),我在tcp :: socket的情况下收到EOF,并且没有收到任何boost :: asio :: ssl :: stream 。 它是错误还是功能? – Amdei

+0

当然,'close_notify'用于干净的关闭,这不会帮助您检测到坏的关闭。我不太了解'boost :: asio :: ssl :: stream',但我的猜测是你正在进行异步操作,SSL/TLS的关闭会导致异步操作的问题(请参阅“正确关闭SSLSocket”链接以上)。这可能会解释一个稍微不同的行为。无论哪种方式,这并不重要。这不会是一个需要修复的bug,因为它不是找到什么时候停止从流/套接字读取的正确方法。 – Bruno

+0

顺便说一句,我不确定你的意思是“* close_notify alert。还有底层的TCP套接字被关闭。*”,但是TLS关闭警告并不意味着底层的TCP套接字必须关闭。 – Bruno

14

SSL_R_SHORT_READ错误预计在这里。当服务器使用SSL_Shutdown启动干净关闭时,会向客户端发送关闭通知关闭警报。 Asio实现将此映射为错误,其类别为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中重写的,所以早期版本的行为可能会有所不同。

+0

我看不到一个简短的阅读。远程主机调用shutdown,然后本地主机的读操作将会以eof完成,如果两个主机都调用shutdown,那么关闭的调用将会以eof完成,这些都是ssl :: stream关闭,而不是TCP/IP关闭。证实了这一点。 –

相关问题