【问题标题】:Error reconnecting boost beast (asio) websocket and http connection after disconnect断开连接后重新连接 boost beast (asio) websocket 和 http 连接时出错
【发布时间】:2019-01-13 07:18:42
【问题描述】:

我正在创建一个客户端应用程序,它使用 ssl Websocket 连接和 ssl Http (Keep-Alive) 连接连接到服务器,并且我正在使用 boost::beast 包来做同样的事情。为了检测死连接,我实现了一个简单的乒乓机制。这些都可以正常工作,但是在处理乒乓故障时会出现问题。问题如下:

为了测试我的代码,我连接到远程服务器,发送了几条消息,然后关闭了我的 wifi。正如预期的那样,在一段时间后,它检测到它没有收到来自服务器的任何消息,并尝试为 http 连接执行async_shutdown,为 websocket 连接执行async_close。我注意到的第一件事是,这两个调用都会阻塞它们各自的线程,直到 wifi 恢复。

在 wifi 启动后,应用程序会在重新连接之前尝试重置流:

void HttpKeepAliveConnection::recreateSocket()
{
    _receivedPongForLastPing = true;
    _sslContext.reset(new boost::asio::ssl::context({boost::asio::ssl::context::sslv23_client}));
    _stream.reset(new HttpStream(_ioContext, *_sslContext));
}

并为 websocket 重置 ws 变量:

void WebsocketConnection::recreateSocket()
{
    _receivedPongForLastPing = true;
    _sslContext.reset(new boost::asio::ssl::context({boost::asio::ssl::context::sslv23_client}));
    _ws.reset(new WebSocket(_ioContext, *_sslContext));
}

不幸的是,它在 on_connect 或 on_ssl_handshake 中都失败了。以下是我的日志:

156 AsioConnectionBase.cpp:53 (2018-08-06 15:34:38.458536) [0x00007ffff601e700] :开始连接序列。连接名称:HttpKeepAliveConn

157 AsioConnectionBase.cpp:122 (2018-08-06 15:34:38.459802) [0x00007ffff481b700] :无法建立到目标的连接。连接失败。连接名称:HttpKeepAliveConn。主持人:xxxxxxxxx。端口:443。错误:操作已取消

158 APIManager.cpp:175 (2018-08-06 15:34:38.459886) [0x00007ffff481b700] : 收到来自连接的错误回调。一秒重启连接。连接名称:HttpKeepAliveConn

159 AsioConnectionBase.cpp:53 (2018-08-06 15:34:39.460009) [0x00007ffff481b700] :开始连接序列。连接名称:HttpKeepAliveConn

160 HttpKeepAliveConnection.cpp:32 (2018-08-06 15:34:39.460515) [0x00007ffff481b700]:ssl 握手失败。连接失败。连接名称:HttpKeepAliveConn。主持人:xxxxxxxxx。端口:443。错误:错误的文件描述符

161 APIManager.cpp:175 (2018-08-06 15:34:39.460674) [0x00007ffff481b700] : 收到来自连接的错误回调。一秒重启连接。连接名称:HttpKeepAliveConn

所以我有两个问题:

  1. 如果 Internet 出现故障并且无法正确关闭 tcp,我们如何关闭连接。
  2. 在重新连接 boost::beast 中的变量之前(或者就此而言,boost::asio 因为 boost::beast 构建在 asio 之上)需要重置

已经尝试调试了几个小时。任何帮助表示赞赏

编辑

所以我想出了我哪里出错了。 Alan Birtles 和 Vinnie Falco 都是对的。在您的 ping 计时器到期(并且没有任何处理程序尚未返回)后关闭死 ssl 连接的方法是

  1. 在您的计时器处理程序中
_stream->lowest_layer().close();

对于网络套接字

_ws->lowest_layer().close();
  1. 等待您的处理程序之一(通常是读取处理程序)返回错误(通常是 boost::asio::error::operation_aborted 错误)。从那里,排队开始下一次重新连接。 (不要在第 1 步之后立即排队重新连接,这会导致我遇到的内存问题。我知道这是 asio 101,但很容易忘记)

  2. 要重置套接字,只需要重置流即可

_stream.reset(new HttpStream(_ioContext, _sslContext));

对于网络套接字

_ws.reset(new WebSocket(_ioContext, _sslContext));

【问题讨论】:

  • shutdown 执行干净的 tcp 关闭并要求将数据包发送到套接字的另一端,并且只能在连接正常结束时使用。如果您的连接中断,您应该调用close 来清理本地资源。
  • Alan Birtles,我尝试了你的建议。对于 Http 做了:_stream->next_layer().lowest_layer().cancel(); _stream->next_layer().lowest_layer().close();它解决了链阻塞的问题。但是重新连接仍然没有发生。我怀疑其他一些状态变量也需要重置。

标签: c++ boost tcp boost-asio boost-beast


【解决方案1】:

我认为asio::ssl::stream关闭后不能再使用了。

如果互联网中断并且无法正确关闭 tcp,我们如何关闭连接。

只允许销毁套接字或流对象。

【讨论】:

  • 我将流变量存储在 scoped_ptr 中并在上面的 recreateSocket() 函数中重置它。在将 _sslContext 存储为 scoped_ptr 后,我也在重置它(只是为了确定)。但它仍然给出错误并继续重新连接循环。
  • 这里有同样的问题。不知道如何解决
  • SSL 示例对您有用吗?尝试从那里开始作为基础,然后修改它们以满足您的需求。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2010-12-31
  • 2021-10-17
  • 1970-01-01
  • 2012-08-07
  • 2019-02-22
  • 1970-01-01
相关资源
最近更新 更多