我正在实现一个使用boost::asio来实现TLS连接库的类。

我仅实现同步操作,其中一些接受超时。如以下示例中所述,我使用了duration_timer和io_service.run_one实现超时方法。

我的问题是一种方法,该方法从套接字中精确地读取“ 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
修复了注释中提到的错误。

最佳答案

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

07-28 04:45