2014-03-31 81 views
0

我正在使用boost::asio與運行在同一臺計算機上的node.js TCP服務器應用程序建立同步TCP套接字連接。我正在Windows上使用Embarcadero RAD studio XE4構建使用Embarcadero集成boost版本1.50的64位應用程序。無法檢測正在關閉的C++同步boost :: asio :: ip :: tcp :: socket連接

事情工作正常,除非node.js TCP服務器關閉。發生這種情況時,我從套接字讀取時,C++客戶端應用程序不檢測到斷開連接。但是,當我寫入套接字時它會檢測到斷開連接。

我現在的代碼是在嘗試理解SO上的boost文檔和各種答案後編寫的。代碼的讀取部是如下(I已省略了緊湊的錯誤代碼的檢查)

boost::system::error_code ec; 
boost::asio::io_service io_service; 
boost::asio::ip::tcp::socket s(io_service); 
boost::asio::ip::tcp::endpoint ep(boost::asio::ip::address::from_string("127.0.0.1"),m_port); 

ec = s.connect(ep,ec); 
std::size_t bytes_available = s.available(ec); 
std::vector<unsigned char> data(bytes_available,0); 

size_t read_len = boost::asio::read(s, boost::asio::buffer(data),boost::asio::transfer_at_least(bytes_available),ec); 

if ((boost::asio::error::eof == ec) || (boost::asio::error::connection_reset == ec)) 
{ 
    // disconnection 
    break; 
} 

這不是一個很大的系統,因爲它是在其自己的線程中運行的環路和輪詢數據內不斷直到程序關閉。在沒有數據的情況下,通常我不會在套接字上執行read(),但是在這種情況下我會這樣做,因爲所有文檔都使我相信只有在執行讀取或寫入套接字時纔會檢測到套接字斷開連接。問題在於,上面的代碼在node.js應用程序關閉時根本不會檢測到斷開連接。如果我正在寫,我發現它(代碼的寫入部分使用相同的boost :: asio :: error`錯誤作爲檢測讀取),但讀取時不會。

很明顯,我無法執行讀取的字節數量大於可用的或我的線程將阻止,我將無法執行後來寫入我的線程循環。

我是否缺少另一個特定的增強錯誤代碼來檢測錯誤情況?或者這是零長度讀取特別的問題。如果是這種情況,我還有其他選擇嗎?

目前我得到的node.js服務器寫出一個特定的消息,當它關閉時,我檢測到,然後關閉客戶端自己的套接字。但是,這有點破解,如果可能的話,我寧願採用乾淨的方式來檢測斷開連接。

回答

5

一般來說,Boost.Asio的的read()函數返回時,可以:

  • 緩衝區已​​滿。
  • 完整的條件已得到滿足。
  • 發生錯誤。

它沒有指定檢查這些條件的順序。但是,上次我查看實現時,Boost.Asio在嘗試從套接字讀取數據之前將流中零字節讀取爲無操作的操作。因此,將不會觀察到文件錯誤的結尾,因爲0大小的緩衝區被視爲已滿。

儘管使用異步操作可能會提供更好的結果和可伸縮性,但仍然可以使用同步操作完成以非阻塞方式檢測斷開連接。默認情況下,同步操作是阻塞的,但可以通過功能來控制此行爲。該文件指出:

如果true,插座的同步操作將boost::asio::error::would_block,如果他們無法立即執行請求的操作失敗。如果false,同步操作將阻塞,直到完成。

因此,如果同步操作設置爲不阻塞,並且讀取操作嘗試讀取最小1個字節,則可以以非阻塞方式觀察斷開連接。


下面是一個完整示例演示非阻塞同步read()操作,檢測斷開。爲了節制打印消息,我選擇在操作被阻塞(即有連接但沒有數據可讀)時執行睡眠。

#include <algorithm> 
#include <iostream> 
#include <vector> 
#include <boost/asio.hpp> 
#include <boost/thread.hpp> 

int main(int argc, char* argv[]) 
{ 
    if (argc != 2) 
    { 
    std::cerr << "Usage: <port>\n"; 
    return 1; 
    } 

    // Create socket and connet to local port. 
    namespace ip = boost::asio::ip; 
    boost::asio::io_service io_service; 
    ip::tcp::socket socket(io_service); 
    socket.connect(ip::tcp::endpoint(
     ip::address::from_string("127.0.0.1"), std::atoi(argv[1]))); 

    // By setting the socket to non-blocking, synchronous operations will 
    // fail with boost::asio::error::would_block if they cannot immediately 
    // perform the requested operation. 
    socket.non_blocking(true); 

    // Synchronously read data. 
    std::vector<char> data; 
    boost::system::error_code ec; 
    for (;;) 
    { 
    // Resize the buffer based on the amount of bytes available to be read. 
    // Guarantee that the buffer is at least 1 byte, as Boost.Asio treats 
    // zero byte read operations as no-ops. 
    data.resize(std::max<std::size_t>(1, socket.available(ec))); 

    // Read all available data. 
    std::size_t bytes_transferred = 
     boost::asio::read(socket, boost::asio::buffer(data), ec); 

    // If no data is available, then continue to next iteration. 
    if (bytes_transferred == 0 && ec == boost::asio::error::would_block) 
    { 
     std::cout << "no data available" << std::endl; 
     boost::this_thread::sleep_for(boost::chrono::seconds(3)); 
     continue; 
    }    

    std::cout << "Read: " << bytes_transferred << " -- "; 
    if (bytes_transferred) 
    { 
     std::cout.write(&data[0], bytes_transferred); 
     std::cout << " -- "; 
    } 
    std::cout << ec.message() << std::endl; 

    // On error, such as a disconnect, exit the loop. 
    if (ec && ec != boost::asio::error::would_block) 
    { 
     break; 
    } 
    } 
} 

下面的輸出是由示例程序連接到寫道:「測試」,「更多的測試」服務器產生,然後關閉了連接:

no data available 
Read: 4 -- test -- Success 
no data available 
Read: 12 -- more testing -- Success 
Read: 0 -- End of file 
+0

謝謝您的回答。不幸的是,它不能解決我的問題,因爲即使沒有讀取任何東西,我也需要重新寫回套接字。我的應用程序有點像WebSocket,即使沒有收到任何讀取數據,我也需要將數據推回到另一端。因此,我無法承擔任何**封鎖電話。也許我現在的方案只需要更改爲異步。 – mathematician1975

+0

@ mathematician1975異步可能是理想的解決方案,但這仍然可以通過同步調用完成。我已經更新了答案,以演示如何以非阻塞方式觀察與同步'read()'操作的斷開連接。 –

+0

非常感謝您的更新。我沒有遇到將套接字設置爲非阻塞的選項,所以這非常有幫助。 – mathematician1975

相關問題