2

I am creating a client application that connects to a server using a an ssl Websocket connection and an ssl Http (Keep-Alive) connection and I am using boost::beast package to do the same. So as to detect a dead connection i have implemented a simple ping-pong mechanism. These all work fine, but an issue comes up when handling the ping-pong failure. The issue is as follows:

For testing my code i connected to the remote server, sent few messages and then turned off my wifi. As expected after a certain period it detected that it did not receive any message from the server and it tried to do an async_shutdown for the http connection and an async_close for the websocket connection. First thing i noticed was that both these calls block their respective strands until the wifi is back up.

And after the wifi is up, the application tries to reset the stream before reconnect:

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

And reset ws variable for websocket:

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

Unfortunately it fails at either on_connect or on_ssl_handshake. Following are my logs:

156 AsioConnectionBase.cpp:53 (2018-08-06 15:34:38.458536) [0x00007ffff601e700] : Started connect sequence. Connection Name: HttpKeepAliveConn

157 AsioConnectionBase.cpp:122 (2018-08-06 15:34:38.459802) [0x00007ffff481b700] : Failed establishing connection to destination. Connection failed. Connection Name: HttpKeepAliveConn. Host: xxxxxxxxx. Port: 443. Error: Operation canceled

158 APIManager.cpp:175 (2018-08-06 15:34:38.459886) [0x00007ffff481b700] : Received error callback from connection. Restarting connection in a sec. Connection Name: HttpKeepAliveConn

159 AsioConnectionBase.cpp:53 (2018-08-06 15:34:39.460009) [0x00007ffff481b700] : Started connect sequence. Connection Name: HttpKeepAliveConn

160 HttpKeepAliveConnection.cpp:32 (2018-08-06 15:34:39.460515) [0x00007ffff481b700] : Failed ssl handshake. Connection failed.Connection Name: HttpKeepAliveConn. Host: xxxxxxxxx. Port: 443. Error: Bad file descriptor

161 APIManager.cpp:175 (2018-08-06 15:34:39.460674) [0x00007ffff481b700] : Received error callback from connection. Restarting connection in a sec. Connection Name: HttpKeepAliveConn

So I have 2 questions:

  1. How do we close a connection if internet is down and a proper tcp close is not possible.
  2. Before reconnecting what are the variables in boost::beast (or for that matter boost::asio as boost::beast is built on top of asio) that needs to be reset

Have been stuck trying to debug this for couple of hours. Any help is appreciated

EDIT

So I figured out where I went wrong. Both Alan Birtles and Vinnie Falco were right. The way to close a dead ssl connection after your ping timer has expired (and none of the handlers have returned yet) is

  1. In your timer handler
_stream->lowest_layer().close();

For websocket

_ws->lowest_layer().close();
  1. Wait for one of your handlers (typically read handler) to return with error (typically boost::asio::error::operation_aborted error). From there, queue the start of the next reconnect. (Do not queue the reconnect immediately after step 1, it will result in memory issues that I faced. I know this is asio 101, but is easy to forget)

  2. For resetting socket, all that is required is for the stream to be reset

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

For websocket

_ws.reset(new WebSocket(_ioContext, _sslContext));
john_zac
  • 1,241
  • 2
  • 11
  • 16
  • 1
    `shutdown` does a clean tcp close and requires packets to be sent to the other side of the socket and should only be used at the normal ending of a connection. If your connection is broken you should just call `close` to cleanup the local resources. – Alan Birtles Aug 06 '18 at 10:58
  • Alan Birtles, I tried your suggestion. For Http did: _stream->next_layer().lowest_layer().cancel(); _stream->next_layer().lowest_layer().close(); It solved the issue of blocking of strand. But reconnect still doesn't happen. I suspect some other state variable need to be reset as well. – john_zac Aug 06 '18 at 11:38

1 Answers1

1

I don't think asio::ssl::stream can be used again after being closed.

How do we close a connection if internet is down and a proper tcp close is not possible.

Simply allow the socket or stream object to be destroyed.

Vinnie Falco
  • 5,173
  • 28
  • 43
  • 2
    I am storing the stream variable in a scoped_ptr and resetting it in the recreateSocket() function above. I am also resetting _sslContext after storing it as a scoped_ptr (just to be sure). But it still gives error and goes on a reconnect loop. – john_zac Aug 06 '18 at 12:02
  • same issue here. not sure how to fix – miles.sherman Aug 22 '18 at 18:53
  • Do the SSL examples work for you? Try starting there as a base and then modify them to suit your needs. – Vinnie Falco Aug 24 '18 at 18:40