Collectives™ on Stack Overflow
Find centralized, trusted content and collaborate around the technologies you use most.
Learn more about Collectives
Teams
Q&A for work
Connect and share knowledge within a single location that is structured and easy to search.
Learn more about Teams
I'm new to beast and trying to create a websocket client which will subscribe to a websocket event in the remote server and start to listen for incoming messages forever unless I or the server explicitly close the connection.
For now, I am using the the async_sll_websocket_client example that is in the github
repository
of the beast.
#include "example/common/root_certificates.hpp"
#include <boost/beast/core.hpp>
#include <boost/beast/ssl.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/websocket/ssl.hpp>
#include <boost/asio/strand.hpp>
#include <cstdlib>
#include <functional>
#include <iostream>
#include <memory>
#include <string>
namespace beast = boost::beast; // from <boost/beast.hpp>
namespace http = beast::http; // from <boost/beast/http.hpp>
namespace websocket = beast::websocket; // from <boost/beast/websocket.hpp>
namespace net = boost::asio; // from <boost/asio.hpp>
namespace ssl = boost::asio::ssl; // from <boost/asio/ssl.hpp>
using tcp = boost::asio::ip::tcp; // from <boost/asio/ip/tcp.hpp>
//------------------------------------------------------------------------------
// Report a failure
fail(beast::error_code ec, char const* what)
std::cerr << what << ": " << ec.message() << "\n";
// Sends a WebSocket message and prints the response
class session : public std::enable_shared_from_this<session>
tcp::resolver resolver_;
websocket::stream<
beast::ssl_stream<beast::tcp_stream>> ws_;
beast::flat_buffer buffer_;
std::string host_;
std::string text_;
public:
// Resolver and socket require an io_context
explicit
session(net::io_context& ioc, ssl::context& ctx)
: resolver_(net::make_strand(ioc))
, ws_(net::make_strand(ioc), ctx)
// Start the asynchronous operation
char const* host,
char const* port,
char const* text)
// Save these for later
host_ = host;
text_ = text;
// Look up the domain name
resolver_.async_resolve(
host,
port,
beast::bind_front_handler(
&session::on_resolve,
shared_from_this()));
on_resolve(
beast::error_code ec,
tcp::resolver::results_type results)
if(ec)
return fail(ec, "resolve");
// Set a timeout on the operation
beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
// Make the connection on the IP address we get from a lookup
beast::get_lowest_layer(ws_).async_connect(
results,
beast::bind_front_handler(
&session::on_connect,
shared_from_this()));
on_connect(beast::error_code ec, tcp::resolver::results_type::endpoint_type ep)
if(ec)
return fail(ec, "connect");
// Update the host_ string. This will provide the value of the
// Host HTTP header during the WebSocket handshake.
// See https://tools.ietf.org/html/rfc7230#section-5.4
host_ += ':' + std::to_string(ep.port());
// Set a timeout on the operation
beast::get_lowest_layer(ws_).expires_after(std::chrono::seconds(30));
// Perform the SSL handshake
ws_.next_layer().async_handshake(
ssl::stream_base::client,
beast::bind_front_handler(
&session::on_ssl_handshake,
shared_from_this()));
on_ssl_handshake(beast::error_code ec)
if(ec)
return fail(ec, "ssl_handshake");
// Turn off the timeout on the tcp_stream, because
// the websocket stream has its own timeout system.
beast::get_lowest_layer(ws_).expires_never();
// Set suggested timeout settings for the websocket
ws_.set_option(
websocket::stream_base::timeout::suggested(
beast::role_type::client));
// Set a decorator to change the User-Agent of the handshake
ws_.set_option(websocket::stream_base::decorator(
[](websocket::request_type& req)
req.set(http::field::user_agent,
std::string(BOOST_BEAST_VERSION_STRING) +
" websocket-client-async-ssl");
// Perform the websocket handshake
ws_.async_handshake(host_, "/",
beast::bind_front_handler(
&session::on_handshake,
shared_from_this()));
on_handshake(beast::error_code ec)
if(ec)
return fail(ec, "handshake");
// Send the message
ws_.async_write(
net::buffer(text_),
beast::bind_front_handler(
&session::on_write,
shared_from_this()));
on_write(
beast::error_code ec,
std::size_t bytes_transferred)
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "write");
// Read a message into our buffer
ws_.async_read(
buffer_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
on_read(
beast::error_code ec,
std::size_t bytes_transferred)
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "read");
//here I try to read the buffer again instead of ccalling the async_close.
ws_.async_read(
buffer_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
// Close the WebSocket connection
//ws_.async_close(websocket::close_code::normal,
// beast::bind_front_handler(
// &session::on_close,
// shared_from_this()));
on_close(beast::error_code ec)
if(ec)
return fail(ec, "close");
// If we get here then the connection is closed gracefully
// The make_printable() function helps print a ConstBufferSequence
std::cout << beast::make_printable(buffer_.data()) << std::endl;
//------------------------------------------------------------------------------
int main(int argc, char** argv)
// Check command line arguments.
if(argc != 4)
std::cerr <<
"Usage: websocket-client-async-ssl <host> <port> <text>\n" <<
"Example:\n" <<
" websocket-client-async-ssl echo.websocket.org 443 \"Hello, world!\"\n";
return EXIT_FAILURE;
auto const host = argv[1];
auto const port = argv[2];
auto const text = argv[3];
// The io_context is required for all I/O
net::io_context ioc;
// The SSL context is required, and holds certificates
ssl::context ctx{ssl::context::tlsv12_client};
// This holds the root certificate used for verification
load_root_certificates(ctx);
// Launch the asynchronous operation
std::make_shared<session>(ioc, ctx)->run(host, port, text);
// Run the I/O service. The call will return when
// the socket is closed.
ioc.run();
return EXIT_SUCCESS;
So far, I tried to call the async_read
again in the on_read
function and invoke the on_read
recursively. The connection doesn't close this way but ``on_read```doesn't get invoked second time. Is there a pattern which I can apply to continuously listen to incoming messages from server??
EDIT: Apparently my approach was correct, I just needed to send subscribe message one more time after the first on_read
call.
This function might have terminated immediately after the first read which was the response for the write(code).
Try this code, to keep looking for message as long as the socket is open.
net::io_context ioc;
auto const host = "localhost";
auto const port = "1234";
auto const port = "SOME TEXT";
// Launch the asynchronous operation
std::shared_ptr<session> p = std::make_shared<session>(ioc);
p->run(host, port, text);
ioc.run();
while (p->is_socket_open()) {
ioc.restart();
p->enable_async_read();
ioc.run();
implementation for enable_async_read & is_socket_open
void session::enable_async_read() {
ws_.async_read(
buffer_,
beast::bind_front_handler(
&session::on_read,
shared_from_this()));
bool session::is_socket_open() {
return ws_.is_open();
Thanks for contributing an answer to Stack Overflow!
- Please be sure to answer the question. Provide details and share your research!
But avoid …
- Asking for help, clarification, or responding to other answers.
- Making statements based on opinion; back them up with references or personal experience.
To learn more, see our tips on writing great answers.