代码之家  ›  专栏  ›  技术社区  ›  Dan

boost::asio io_服务::run_one导致分段错误

  •  0
  • Dan  · 技术社区  · 7 年前

    boost::asio 实现TLS连接库。

    超时 . 我使用deadline\u计时器和io\u服务实现超时方法。运行一个 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 修复了评论中提到的错误。

    2 回复  |  直到 7 年前
        1
  •  1
  •   user7860670    7 年前

    你把指针和对象生命周期管理搞砸了。如果 connect auto_ptr unique_ptr 而是管理拥有的指针。

        2
  •  0
  •   Dan    7 年前

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

    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_service 这个 deadline_timer context 连接时创建,断开时释放。我仍然不明白发生了什么,让我解释一下:

    我已经试着一个接一个地重新实现上述变量,也就是说,首先是 插座 ,然后 timer 上下文 最后 io_服务

    io_服务 是ptr,但我不明白为什么。如果io_服务是类作用域变量,那么每次类实例超出作用域时,也就是每次我的一个测试完成时,都应该删除它。

    readNBytes 由于超时而引发异常 read_async 通话仍保留在 io_服务 操作队列,可能是这导致了问题。