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
修复了评论中提到的错误。