Комплексная обработка ошибок - PullRequest
3 голосов
/ 28 апреля 2010

У меня есть особенно ужасный кусок сетевого кода. Я использую Asio, но это действительно не имеет значения для этого вопроса. Я предполагаю, что нет другого способа отсоединить сокет, кроме как закрыть его. Проблема в том, что open(), bind() и listen() могут все бросить system_error. Поэтому я обработал код простым try/catch. Код, написанный на ломаном.

using namespace boost::asio;

class Thing
{
public:

   ip::tcp::endpoint m_address;

   ip::tcp::acceptor m_acceptor;

   /// connect should handle all of its exceptions internally.
   bool connect()
   {
      try
      {
         m_acceptor.open( m_address.protocol() );
         m_acceptor.set_option( tcp::acceptor::reuse_address(true) );

         m_acceptor.bind( m_address );
         m_acceptor.listen();

         m_acceptor.async_accept( /*stuff*/ );
      }
      catch( const boost::system::system_error& error )
      {
         assert(acceptor.is_open());
         m_acceptor.close();
         return false;
      }
      return true;
   }

   /// don't call disconnect unless connect previously succeeded.
   void disconnect()
   {
      // other stuff needed to disconnect is ommited
      m_acceptor.close();
   }
};

Ошибка в том, что если сокету не удается подключиться, он попытается закрыть его в блоке catch и выдать еще один системный ошибку о закрытии акцептора, который никогда не был открыт.

Одним из решений является добавление if( acceptor.is_open() ) в блок catch, но на вкус это неправильно. Вроде как смешивание проверки ошибок C в стиле с c++ исключениями. Если я пойду по этому пути, я также могу использовать версию «1014» *.

без броска.
boost::system::error_code error;
acceptor.open( address.protocol, error );
if( ! error )
{
    try
    {
       acceptor.set_option( tcp::acceptor::reuse_address(true) );

       acceptor.bind( address );
       acceptor.listen();

       acceptor.async_accept( /*stuff*/ );
    }
    catch( const boost::system::system_error& error )
    {
       assert(acceptor.is_open());
       acceptor.close();
       return false;
    }
}
return !error;

Существует ли элегантный способ обработки этих возможных исключений с использованием блоков RAII и try/catch?

Я просто ошибаюсь, пытаясь избежать обработки ошибок в стиле if( error condition ) при использовании исключений?

Ответы [ 2 ]

3 голосов
/ 02 мая 2010

Я бы предложил просто выполнить отдельную обработку ошибок для open, так как до и после очистки различается:

bool connect()
{
  try {
     m_acceptor.open( m_address.protocol() );
  } catch( const boost::system::system_error& error ) {
     return false;
  }

  try {
     m_acceptor.set_option( tcp::acceptor::reuse_address(true) );

     m_acceptor.bind( m_address );
     m_acceptor.listen();

     m_acceptor.async_accept( /*stuff*/ );
  } catch( const boost::system::system_error& error ) {
     m_acceptor.close();
     return false;
  }
  return true;
}
2 голосов
/ 28 апреля 2010

С помощью try-catch вы можете принять во внимание, что system_error имеет код ошибки, который дает вам реальную причину. Таким образом, вы можете проверить этот код ошибки в предложении catch.

Чтобы использовать RAI, вам нужно выполнить соединение на конструкторе и отключить на деструкторе, но я не знаю, что стоит за

acceptor.async_accept( /*stuff*/ );

так что, возможно, вам нужно не пускать эту часть. Вещь th;

{
 Connector conn(th); / connect on constructor
 // ... th.async_accept  
 // do some work while connected
}
 // disconnect on destructor

Соединитель позаботится о том, открыт акцептор или нет, используя конкретную переменную-член is_open, которая устанавливается сразу после успешного выполнения acceptor.open ().

   Connector::Connector(...)
   : ...
   , is_open(false)
   {
     m_acceptor.open( m_address.protocol() );
     is_open=true;
     m_acceptor.set_option( tcp::acceptor::reuse_address(true) );

     m_acceptor.bind( m_address );
     m_acceptor.listen();

     m_acceptor.async_accept( /*stuff*/ );
   }

   Connector::~Connector(...)
   {
     // other stuff needed to disconnect is omitted
     if (is_open) m_acceptor.close();
   }
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...