How to send websocket message while waiting to receive?

229 Views Asked by At

My goal is to register to a websocket service to get real-time company quotations.

So I based my code on the following example, by mostly calling (again) async_read, once we receive a quotation to accept futures quotation:

https://www.boost.org/doc/libs/master/libs/beast/example/websocket/client/async-ssl/websocket_client_async_ssl.cpp

The problem is when I am waiting for a new quotation (who could take sometimes minutes or hours for small companies), the program is blocked waiting for a message and I do not have the opportunity to ask for another company.

I tried to use the "post" function to call again async_write in the good context thread but the program crashed.

Is there any way to force the completion of callback on_read, to have then the opportunity to send a new message?

Here is the function I modified (simplified without mutexes):

void
    on_read(
        beast::error_code ec,
        std::size_t bytes_transferred)
    {
        boost::ignore_unused(bytes_transferred);

        if(ec)
            return fail2(ec, "read");

        
        std::string mycontent = beast::buffers_to_string(buffer_.data());
        cout << mycontent << endl;
        buffer_.clear();
        
        ws_.async_read(
            buffer_,
            beast::bind_front_handler(
                &session::on_read,
                shared_from_this()));
    }

    void subscribe(const std::string &symbol)
    {
        // We save the message in the queue
        std::string text = "{\"action\": \"subscribe\", \"symbols\": \"" + symbol + "\"}";
        msgqueue_.push_back(text);
        boost::asio::post(ioc_, beast::bind_front_handler(&session::_subscription_to_post, shared_from_this()));
    }

    void _subscription_to_post()
    {
        if (msgqueue_.empty())
            return;

        // We send the message
        ws_.async_write(
            net::buffer(msgqueue_.front()),
            beast::bind_front_handler(
                &session::on_write,
                shared_from_this()));
        msgqueue_.pop_front();
    }

And the program crashes immediately when trying to async_write.

1

There are 1 best solutions below

5
On

The problem is when I am waiting for a new quotation [...] the program is blocked waiting for a message

It isn't technically blocked because you are using async_read

I tried to use the "post" function to call again async_write in the good context thread, but the program crashed.

That means you're doing something wrong. You can post a question with your self-contained minimal code, and we can tell you what is wrong.

In general, you can use a single read operation and a single write operation concurrently (as in: in flight, asynchronously, you still need to synchronize threads accessing all related resources).

Typically, you have a single async-read-chain active at all times, and a outbound message-queue that is drained by a single async-write chain (that obviously ends when the queue is empty, so needs to be initiated when the first outbound message is queued).

I have many answers on this site (literally dozens) that you may be able to find by search for outbox or outbox_ e.g. Keep in mind that the majority of them will deal with plain (ssl) sockets intead of websockets, but the pattern practically the same.

Is there any way to force the completion of callback on_read, to have then the opportunity to send a new message ?

You can technically cancel() it, which completes it with operation_aborted. But that is not what you need. You want full-duplex, so writing cancel() is the opposite of what you want.