J'implémente une classe qui utilise boost::asio
pour implémenter une bibliothèque pour les connexions TLS.boost :: asio io_service :: run_one conduit à une erreur Segmentation
Je ne mets en œuvre que des opérations synchrones et certaines d'entre elles acceptent un timeout. je mets en œuvre les méthodes de délai d'attente en utilisant un deadline_timer et io_service.run_one, comme expliqué dans cet exemple: http://www.boost.org/doc/libs/1_45_0/doc/html/boost_asio/example/timeouts/async_tcp_client.cpp
Mon problème est une méthode qui lit octets exactement « n » de la prise et accepte un délai d'attente en tant que paramètre. Le problème est que le io_service.run_one()
élève un SIGSEV
et je ne sais pas pourquoi. Voici le code (il est si long, mais je ne sais pas d'autre meilleure façon d'expliquer cela):
Le code
ci-dessous sont les méthodes utilisés pour le test, je suis d'exécution:
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;
}
}
le test
En dessous est le test que je suis en cours d'exécution pour tester la méthode:
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);
}
}
Le problème
Dans le test ci-dessus, la première itération de la boucle va bien, c'est-à-dire que la première fois que la méthode readNBytes
est appelée, elle fonctionne (lève une exception comme prévu). La deuxième fois qu'il est exécuté, il soulève le SIGSEV
.
EDIT
Je suis le test ci-dessus exécutais entre autres qui testent d'autres fonctionnalités. J'ai réalisé que si j'exécute le test ci-dessus seulement, cela fonctionne. Mais, si je l'exécute en plus d'autres, alors le programme se bloque avec le SIGSEV
mentionné.
Ceci est l'un des tests qui cause le problème:
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());
}
}
En conclusion, si j'exécute les tests ci-dessus, la première plante. Mais si j'exécute seulement le premier, cela fonctionne.
EDIT 2 Correction du bug mentionné dans les commentaires.
Je peux ' t d'utiliser ni C++ 11 ni stimuler smart ptrs pour l'instant, mais oui je me suis rendu compte que je préfère les ptrs bruts que 'auto_ptr' – Dan