如何优雅地关闭 boost asio ssl 客户端?

Posted

技术标签:

【中文标题】如何优雅地关闭 boost asio ssl 客户端?【英文标题】:How to gracefully shutdown a boost asio ssl client? 【发布时间】:2014-04-29 19:08:34 【问题描述】:

客户端进行了一些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() 处于活动状态 - 但用户决定退出客户端(例如,通过标准输入的命令)。当从该上下文调用async_shutdown() 时,会发生以下情况:

async_read_some() 回调执行时带有“短读”错误代码 - 现在是预期的 async_shutdown() 回调执行时带有 decryption failed or bad record mac 错误代码 - 这是意料之外的

服务端不报错。

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

【问题讨论】:

【参考方案1】:

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

a) 从标准输入处理程序调用内部:

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

b) 这会导致 async_read_some() 回调以“短读”“错误”代码执行

c) 在那个“错误”条件下的回调中 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() 回调执行时带有“短读”错误代码,我们最终从这里调用:

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

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

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

SSL3 警报读取:警告:关闭通知 完毕 关闭 SSL 连接关闭 接受

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

另类

除了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();

【讨论】:

以上是关于如何优雅地关闭 boost asio ssl 客户端?的主要内容,如果未能解决你的问题,请参考以下文章

优雅地取消 boost::asio::async_read

boost::asio 无法干净地关闭 TCP 连接

boost asio ssl async_shutdown 总是以错误结束?

boost::asio::async_write 写入 ssl::stream 成功但服务器未获取

如何接受boost :: asio :: ssl :: stream 作为boost :: asio :: ip :: tcp :: socket类型的参数

boost::asio 完全断开连接