Let's say we have a websocket client based on boost::beast
. At some point we want to send a ping frame to the server. To do this, boost::beast::websocket::stream
offers two options:
ping
methodasync_ping
methodI want to know how to use these methods correctly in an asynchronous environment.
ping
or async_ping
if there is an outstanding (incompleted) async_write
operation?ping
seems preferable to async_ping
because async_ping
requires us to ensure that there are no outstanding ping, pong, async_ping, or async_pong operations when it is called. If I decide to use async_ping
, what is the correct way to call it?ping
(because ping
is easy in use), how long can a synchronous ping
call theoretically take? Could this be a bottleneck in an asynchronous application?Which of these approaches is preferable to use in an asynchronous environment and how to implement it correctly?
Please note that I am working in a single-threaded but asynchronous environment.
You didn't ask, but mixing async_XXX
and synchronous calls is not supported by beast::websocket::stream
.
No. [async_]ping()
and [async_]close
count as caller-initiated-write-operations. These must not be overlapped with other caller-initiated-write-operations.
As far as automatic control frame behavior is concerned, these do not compete with caller-initiated-write-operations, this is documented here
During read operations, Beast automatically reads and processes control frames. If a control callback is registered, the callback is notified of the incoming control frame. The implementation will respond to pings automatically. The receipt of a close frame initiates the WebSocket close procedure, eventually resulting in the error code error::closed being delivered to the caller in a subsequent read operation, assuming no other error takes place.
However, these writes will not compete with caller-initiated write operations. For the purposes of correctness with respect to the stream invariants, caller-initiated read operations still only count as a read. This means that callers can have a simultaneously active read, write, and ping/pong operation in progress, while the implementation also automatically handles control frames.
As mentioned above, in async context ping()
is out of the question. async_ping
should be queued. My recommendation: the best way to do ping
is probably: not at all. I have yet to think of a reason why it would help. Also see e.g. How to use SO_KEEPALIVE option properly to detect that the client at the other end is down?
You can't. How long ping() can take depends (only) on the peer implementation. See also Auto-fragment
For some inspiration, beyond making a barrage of isolated questions consider looking at existing examples. E.g. I've recently answered a similar question to show how close
operations (so, using async_close
) are write operations as well, and how to elegantly combine them with a write queue: WebSocket async_close fails with "Operation canceled" in destructor (Boost.Beast)