我有一个特别令人讨厌的网络代码。我正在使用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块中关闭它并抛出另一个关于关闭一个从未打开过的接受器的system_error。
一种解决方案是在catch块中添加if( acceptor.is_open() )
,但尝试错了。有点像混合C
- 样式错误检查与c++
例外。如果我去哪条路线,我也可以使用open()
的非投掷版本。
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 )
样式错误处理?
答案 0 :(得分:3)
我建议只对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;
}
答案 1 :(得分:2)
使用try-catch,您可以考虑到system_error有一个error_code,它给出了真正的原因。所以你可以在catch句子上测试这个error_code。
要使用RAI,您需要在构造函数上进行连接并在析构函数上进行断开连接,但我不知道
背后的内容是什么acceptor.async_accept( /*stuff*/ );
所以也许你需要保留这一部分。 事情;
{
Connector conn(th); / connect on constructor
// ... th.async_accept
// do some work while connected
}
// disconnect on destructor
Connector将使用在acceptor.open()成功之后设置的特定成员变量is_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();
}