我正在實現一個使用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 修正了評論中提到的錯誤。
我可以'現在既不使用C++ 11也不使用智能ptrs,但是我意識到我的原始ptrs比'auto_ptr' – Dan