I followed this link to create a websocket client.
This is the class for that:
#include <boost/beast/websocket.hpp>
#include <boost/asio/connect.hpp>
#include <boost/asio/ip/tcp.hpp>
#include <boost/asio/ssl/stream.hpp>
class  WebsocketClient {
public:
    WebsocketClient(const std::string& hostname, const std::string& port, const std::string& url)
        : m_socket(m_io_context)
    {
        boost::asio::ip::tcp::resolver resolver{ m_io_context };
        auto const results = resolver.resolve(hostname, port);
        auto ep = boost::asio::connect(m_socket.next_layer(), results);
        auto host = hostname + std::string(":") + std::to_string(ep.port());
        m_socket.set_option(boost::beast::websocket::stream_base::decorator(
            [](boost::beast::websocket::request_type& req) {
                req.set(boost::beast::http::field::user_agent,
                        std::string(BOOST_BEAST_VERSION_STRING) +
                        " websocket-client-coro");
            }));
        // Perform the websocket handshake
        m_socket.handshake(host, url);
    }
    std::string read() {
        boost::beast::flat_buffer buffer;
        m_socket.read(buffer);
        char* char_ptr = static_cast<char*>(buffer.data().data());
        return std::string(char_ptr, buffer.size());
    }
    void send(const std::string message) {
        m_socket.write(boost::asio::buffer(message));
    }
void close() {
        if (m_socket.is_open()) {
            m_socket.close(boost::beast::websocket::close_code::normal);
        }
        std::cout << "THIS IS NEVER PRINTED " << std::endl;
}
    ~WebsocketClient() {
        close();
        std::cout << "NEVER REACHED " << std::endl;
    }
private:
    boost::asio::io_context m_io_context;
    boost::beast::websocket::stream<boost::asio::ip::tcp::socket> m_socket;
};
I use crowcpp to create a simple websocket server
int main() {
    crow::SimpleApp app;
    CROW_ROUTE(app, "/").websocket()
        .onaccept([](const crow::request& ) {
            return true;
        })
        .onopen([&](crow::websocket::connection& ) {
            std::cout << "CLIENT OPENED - Server "<< std::endl;
        })
        .onclose([&](crow::websocket::connection& , const std::string&) {
            std::cout << "Client CLOSED - Server "<< std::endl;
        });
    std::future<void> m_async_thread_server;
    m_async_thread_server = app
        .bindaddr("127.0.0.1")
        .signal_clear()
        .port(8080)
        .run_async();
     std::this_thread::sleep_for(std::chrono::milliseconds(100));
    auto client = WebsocketClient("127.0.0.1", "8080", "/");
  }
My question is really whether I explicitly invoke client.close() or let the destructor handle it , the problem remains that my thread never returns after             m_socket.close(boost::beast::websocket::close_code::normal);
By doing a bit of debugging I see that the websocket client is waiting to receive a close header message from the server. By digging a bit into what CrowCpp does after receiving a close request I do see it sends out a close header message.
running the code I get following prints only:
(2023-04-15 12:20:32) [INFO ] Crow/1.0 server is running at http://127.0.0.1:8080 using 2 threads (2023-04-15 12:20:32) [INFO ] Call
app.loglevel(crow::LogLevel::Warning)to hide Info level logs.
Client OPENED - Server
Client CLOSED - Server
