2014-03-31 77 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

相关问题