2017-09-25 267 views
0

我正在实现一个使用boost::asio实现TLS连接库的类。boost :: asio io_service :: run_one导致分段错误

我只实现同步操作,其中一些接受超时我实现使用deadline_timer和io_service.run_one超时方法,如在本实施例中说明:http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp

我的问题是与从插座读取准确“n”个字节,并接受一个超时作为参数的方法。问题是io_service.run_one()正在筹集SIGSEV,我不知道为什么。下面是代码(这是这么长时间,但我不知道有什么别的更好的办法来解释这一点):

代码

下面是参与测试的方法,我执行:

void CMDRboostConnection::check_deadline() 
{ 
    // Check whether the deadline has passed. We compare the deadline against 
    // the current time since a new asynchronous operation may have moved the 
    // deadline before this actor had a chance to run. 
    if (m_timeoutOpsTimer->expires_at() <= boost::asio::deadline_timer::traits_type::now()) 
    { 
    // TODO do I need to cancel async operations? 
    m_timeoutOpsErrorCode = boost::asio::error::timed_out; 

    // There is no longer an active deadline. The expiry is set to positive 
    // infinity so that the actor takes no action until a new deadline is set. 
    m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin); 
    } 

    // Put the actor back to sleep. 
    m_timeoutOpsTimer->async_wait(
     boost::bind(&CMDRboostConnection::check_deadline, this)); 
} 

bool CMDRboostConnection::connect() 
{ 
    // TODO: This method already throws an exception, it should be void. 
    DEBUG("Connecting to " + m_url + " : " + m_port); 
    try 
    { 
    // If the socket is already connected, disconnect it before 
    // opening a new conneciont. 
    if (isConnected()) 
    { 
     disconnect(); 
    } 

    m_socket = new SSLSocket(m_ioService, m_context); 

    tcp::resolver resolver(m_ioService); 
    tcp::resolver::query query(m_url, m_port); 

    tcp::resolver::iterator end; 
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 

    boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query)); 

    if (endpoint_iterator == end) 
    { 
     DEBUG("Endpoint cannot be resolved, disconnecting..."); 
     disconnect(); 
    } 
    else 
    { 
     m_timeoutOpsTimer = new boost::asio::deadline_timer(m_ioService); 
     m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin); 
     // Start the persistent actor that checks for deadline expiry. 
     check_deadline(); 

     DEBUG("Endpoint resolved, performing handshake"); 
     m_socket->set_verify_mode(boost::asio::ssl::verify_none); 
     m_socket->handshake(SSLSocket::client); 

     DEBUG("Handshake done, connected to " + m_url + " : " + m_port); 
     m_isConnected = true; 
    } 
    } 
    catch (boost::system::system_error &err) 
    { 
    disconnect(); 
    throw; 
    } 

    return m_isConnected; 
} 

std::streambuf& CMDRboostConnection::readNBytes(int n, unsigned int timeout) 
{ 
    try 
    { 
    if(!isConnected()) 
    { 
     std::string err = "Cannot read, not connected"; 
     ERROR(err); 
     throw std::logic_error(err); 
    } 

    if(n == 0) 
    { 
     return m_buffer; 
    } 

    m_timeoutOpsTimer->expires_from_now(
     boost::posix_time::milliseconds(timeout)); 

    m_timeoutOpsErrorCode = boost::asio::error::would_block; 

    boost::asio::async_read(
     *m_socket, 
     m_buffer, 
     boost::asio::transfer_exactly(n), 
     boost::bind(
      &CMDRboostConnection::timoutOpsCallback, 
      this, 
      boost::asio::placeholders::error, 
      boost::asio::placeholders::bytes_transferred) 
    ); 

    do 
    { 
     m_ioService.run_one(); 
    } while (m_timeoutOpsErrorCode == boost::asio::error::would_block); 

    if(m_timeoutOpsErrorCode) 
    { 
     throw boost::system::system_error(m_timeoutOpsErrorCode); 
    } 

    return m_buffer; 
    } 
    catch(boost::system::system_error &err) 
    { 
    ERROR("Timeout reached trying to read a message"); 
    disconnect(); 
    throw; 
    } 
} 

void CMDRboostConnection::disconnect() 
{ 
    try 
    { 
    DEBUG("Disconnecting..."); 
    if(isConnected()) 
    { 
     m_socket->shutdown(); 

     DEBUG("Closing socket..."); 
     m_socket->lowest_layer().close(); 

     if(m_socket != NULL) 
     { 
     delete m_socket; 
     m_socket = NULL; 
     } 
    } 

    if(m_timeoutOpsTimer != NULL) 
    { 
     delete m_timeoutOpsTimer; 
     m_timeoutOpsTimer = NULL; 
    } 

    DEBUG("Disconnection performed properly"); 
    m_isConnected = false; 
    } 
    catch (boost::system::system_error &err) 
    { 
    ERROR("Exception thrown, error = " << err.code() << 
     ", category: " << err.code().category().name() << std::endl); 

    m_isConnected = false; 

    throw; 
    } 
} 

测试

下面是我运行测试方法测试:

TEST(CMDRboostConnection, readNbytesTimeoutDoesNotMakeTheProgramCrashWhenTmeout) 
{ 
    std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection = 
      std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
       new CMDR::SSL::CMDRboostConnection("localhost", "9999")); 

    unsigned int sleepInterval = 0; // seconds 
    unsigned int timeout = 10; // milliseconds 
    unsigned int numIterations = 10; 
    std::string msg("delay 500000"); // microseconds 

    if(!m_connection->isConnected()) 
    { 
    m_connection->connect(); 
    } 


    for(unsigned int i = 0; i < numIterations; i++) 
    { 

    if(!m_connection->isConnected()) 
    { 
     m_connection->connect(); 
    } 

    ASSERT_NO_THROW(m_connection->write(msg)); 

    ASSERT_THROW (
     m_connection->readNBytes(msg.size(), timeout), 
     boost::system::system_error); 

    ASSERT_FALSE(m_connection->isConnected()); 

    ASSERT_NO_THROW(m_connection->connect()); 

    sleep(sleepInterval); 
    } 
} 

问题

在上面的测试中,第一次循环迭代成功,也就是第一次调用方法readNBytes时,它工作(按预期引发异常)。第二次执行时,会引发SIGSEV

编辑

我执行等来测试其他功能上面的测试。我意识到,如果我只执行上述测试,它就可以工作。但是,如果我另外执行它,那么程序会崩溃,并提到SIGSEV

这是导致问题的测试之一:

TEST(CMDRboostConnection, canConnectDisconnect) 
{ 
    std::auto_ptr<CMDR::SSL::ICMDRsslConnection> m_connection = 
      std::auto_ptr<CMDR::SSL::ICMDRsslConnection>(
       new CMDR::SSL::CMDRboostConnection("localhost", "9999")); 

    unsigned int sleepInterval = 0; // seconds 
    unsigned int timeout = 1000; // milliseconds 
    unsigned int numIterations = 10; 
    std::string msg("normally"); 


    if(!m_connection->isConnected()) 
    { 
    ASSERT_NO_THROW (m_connection->connect()); 
    } 

    for(unsigned int i = 0; i < numIterations; i++) 
    { 
    ASSERT_NO_THROW(m_connection->disconnect()); 
    sleep(sleepInterval); 
    ASSERT_NO_THROW(m_connection->connect()); 
    } 
} 

总之,如果我执行上述两种测试,第一个崩溃。但是如果我只执行第一个,它就会起作用。

编辑2 修正了评论中提到的错误。

回答

0

我用指针代替了所有的成员属性,现在它可以工作(也就是说,我可以通过我写的所有测试)。该方法断开/连接现在如下:

bool CMDRboostConnection::connect() 
{ 
    // TODO: This method already throws an exception, it should be void. 
    DEBUG("Connecting to " + m_url + " : " + m_port); 
    try 
    { 
    // If the socket is already connected, disconnect it before 
    // opening a new conneciont. 
    if (isConnected()) 
    { 
     disconnect(); 
    } 

    m_ioService = new boost::asio::io_service(); 
    m_timeoutOpsTimer = new boost::asio::deadline_timer(*m_ioService); 
    m_context = new boost::asio::ssl::context(boost::asio::ssl::context::sslv23); 
    m_socket = new SSLSocket(*m_ioService, *m_context); 

    tcp::resolver resolver(*m_ioService); 
    tcp::resolver::query query(m_url, m_port); 

    tcp::resolver::iterator end; 
    tcp::resolver::iterator endpoint_iterator = resolver.resolve(query); 

    boost::asio::connect(m_socket->lowest_layer(), resolver.resolve(query)); 

    if (endpoint_iterator == end) 
    { 
     DEBUG("Endpoint cannot be resolved, disconnecting..."); 
     disconnect(); 
    } 
    else 
    { 
     m_timeoutOpsTimer->expires_at(boost::posix_time::pos_infin); 
     // Start the persistent actor that checks for deadline expiry. 
     check_deadline(); 

     DEBUG("Endpoint resolved, performing handshake"); 
     m_socket->set_verify_mode(boost::asio::ssl::verify_none); 
     m_socket->handshake(SSLSocket::client); 

     DEBUG("Handshake done, connected to " + m_url + " : " + m_port); 
     m_isConnected = true; 
    } 
    } 
    catch (boost::system::system_error &err) 
    { 
    disconnect(); 
    throw; 
    } 

    return m_isConnected; 
} 

void CMDRboostConnection::disconnect() 
{ 
    try 
    { 
    DEBUG("Disconnecting..."); 
    if(isConnected()) 
    { 
     m_socket->shutdown(); 

     DEBUG("Closing socket..."); 
     m_socket->lowest_layer().close(); 

     if(m_socket != NULL) 
     { 
     delete m_socket; 
     m_socket = NULL; 
     } 
    } 

    if(m_timeoutOpsTimer != NULL) 
    { 
     delete m_timeoutOpsTimer; 
     m_timeoutOpsTimer = NULL; 
    } 

    if(m_context != NULL) 
    { 
     delete m_context; 
     m_context = NULL; 
    } 

    if(m_ioService != NULL) 
    { 
     delete m_ioService; 
     m_ioService = NULL; 
    } 

    DEBUG("Disconnection performed properly"); 
    m_isConnected = false; 
    } 
    catch (boost::system::system_error &err) 
    { 
    ERROR("Exception thrown, error = " << err.code() << 
     ", category: " << err.code().category().name() << std::endl); 

    if(m_timeoutOpsTimer != NULL) 
    { 
     delete m_timeoutOpsTimer; 
     m_timeoutOpsTimer = NULL; 
    } 

    if(m_context != NULL) 
    { 
     delete m_context; 
     m_context = NULL; 
    } 

    if(m_ioService != NULL) 
    { 
     delete m_ioService; 
     m_ioService = NULL; 
    } 

    m_isConnected = false; 

    throw; 
    } 
} 

正如你可以看到,现在socket,该io_servicedeadline_timercontext是在连接建立和断开释放。我还是不明白是怎么回事,让我解释一下:

我试图重新实现上述变量一个挨着一个,那就是,首先socket,那么timer,那么context,最后io_service

只有当io_service是ptr时,测试才通过,但我不明白为什么。如果io_service是一个类范围的变量,那么每当类实例超出范围时就应该删除它,也就是说,每当我的TEST完成时。

看来,在实施它作为一个ptr之前,这并没有发生。我怀疑这可能是因为readNBytes由于超时而引发异常时,read_async调用将保留在io_service操作队列中,并且可能导致此问题。

1

你搞乱了指针和对象生存期管理。如果connect方法在已连接时调用,则用new覆盖旧套接字,然后检查它是否已连接或在某处使用。另外auto_ptr已弃用。您应该使用unique_ptr来管理拥有指针。

+0

我可以'现在既不使用C++ 11也不使用智能ptrs,但是我意识到我的原始ptrs比'auto_ptr' – Dan