如何优雅地关闭一个提升asio ssl客户端?

时间:2014-03-22 08:35:37

标签: c++ ssl boost openssl boost-asio

客户端会执行一些ssl::stream<tcp_socket>::async_read_some() / ssl::stream<tcp_socket>::async_write()次呼叫,并且在某些时候需要退出,即需要关闭连接。

调用ssl::stream<tcp_socket>::lowest_layer().close()有效,但(正如预期的那样)服务器(openssl s_server -state ...命令)报告关闭连接时出错。

以正确的方式查看API似乎是致电ssl::stream<tcp_socket>::async_shutdown()

现在基本上有两种情况需要关机:

1)客户端处于async_read_some()回调状态,并对“退出”做出反应。来自服务器的命令。从async_shutdown()拨打电话会产生“短暂阅读”。关机回调中的错误。

这是令人惊讶的,但在谷歌搜索后似乎是正常的行为 - 似乎必须检查它是否是一个真正的错误或不是这样:

// const boost::system::error_code &ec
if (ec.category() == asio::error::get_ssl_category() &&
  ec.value() == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) {
  // -> not a real error, just a normal TLS shutdown
}

TLS服务器似乎很高兴 - 它报告:

DONE
shutting down SSL
CONNECTION CLOSED

2)async_read_some()处于活动状态 - 但用户决定退出客户端(例如,通过stdin命令)。以下情况从该上下文调用async_shutdown()后:

  • async_read_some()回调是通过&#39;短读取来执行的。错误代码 - 现在有点预期
  • 使用解密失败或错误记录mac 错误代码执行async_shutdown()回调 - 这是意外的

服务器端不报告错误。

因此,我的问题是如何使用boost asio正确关闭TLS客户端。

1 个答案:

答案 0 :(得分:6)

解决“解密失败”或“错误记录mac”的一种方法是&#39;第二个上下文的错误代码是:

a)从stdin处理程序调用内部:

ssl::stream<tcp_socket>::lowest_layer()::shutdown(tcp::socket::shutdown_receive)

b)这导致async_read_some()回调通过“短读”来执行。 &#39;错误&#39;代码

c)在该回调下的错误&#39;条件async_shutdown()被称为:

// const boost::system::error_code &ec
if (ec.category() == asio::error::get_ssl_category() &&
    ec.value()    == ERR_PACK(ERR_LIB_SSL, 0, SSL_R_SHORT_READ)) {
  // -> not a real error:
  do_ssl_async_shutdown();
}

d)async_shutdown()回调是通过&#39;短读取来执行的。错误代码,我们最终调用的地方:

ssl::stream::lowest_layer()::close()

这些步骤导致连接关闭,而客户端或服务器端没有任何奇怪的错误消息。

例如,当使用openssl s_server -state ...作为服务器时,它会报告sutdown:

SSL3 alert read:warning:close notify
DONE
shutting down SSL
CONNECTION CLOSED
ACCEPT

(最后一行是因为命令接受新连接)

替代

我们也可以调用

代替lowest_layer()::shutdown(tcp::socket::shutdown_receive)
ssl::stream<tcp_socket>::lowest_layer()::cancel()

启动正常关机。它具有相同的效果,即它产生预定的async_read_some()回调的执行(但具有operation_aborted错误代码)。因此,可以从那里调用async_shutdown()

if (ec.value() == asio::error::operation_aborted) {
  cout << "(not really an error)\n";
  do_async_ssl_shutdown();
}