2011-12-11 69 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關閉。證實了這一點。 –

相關問題