0

I have this situation where I need to have an async_read operation "prepared" for reading before I call a custom function which is sending something on the counter-part of the websocket. I have the completion handler of the async_read wrapped in the same strand to which I post my functor.

The problem is that sometimes on::read is not called, so basically I think that the writeFromCounterpart() is called before the ws_1 was in "reading" state.

My understanding is that strand is guaranteeing the order between completion handlers, but I don't understand if guarantees that the async_* operation is "ready" (running in its thread and reading) before continuing with other operation from the strand FIFO.

Full code bellow: When running it, most of the time I've seen the following output:

on_read called
Run 2 Handlers

I was able to see the following also a few times (~1 in 20 in stress condition),

on_write called
Run 1 Handlers

I never seen both on_write and on_read

Code:

#include <boost/asio.hpp>
#include <boost/beast/websocket.hpp>
#include <boost/beast/http.hpp>
#include <boost/beast/core.hpp>
#include <string>

using tcp = boost::asio::ip::tcp;               // from <boost/asio/ip/tcp.hpp>
namespace websocket = boost::beast::websocket;  // from <boost/beast/websocket.hpp>

boost::beast::multi_buffer buffer;
boost::asio::io_context ioc_1;
boost::asio::io_context ioc_2;
websocket::stream<tcp::socket> ws_1(ioc_1);
websocket::stream<tcp::socket> ws_2(ioc_2);


void on_write(boost::system::error_code, std::size_t) {
  std::cout << "on_write called" << std::endl << std::flush;
}

void writeFromCounterpart() {
  ws_2.async_write(boost::asio::buffer(std::string("Hello")), on_write);
}

void on_read(boost::system::error_code, std::size_t) {
  std::cout << "on_read called" <<std::endl << std::flush;
}

int main() {
  std::thread t([](){
    auto const address = boost::asio::ip::make_address("127.0.0.1");
    tcp::acceptor acceptor{ioc_2, {address, 30000}};
    tcp::socket socket{ioc_2};

    acceptor.accept(socket);
    websocket::stream<tcp::socket> ws{std::move(socket)};
    ws.accept();
    ws_2 = std::move(ws);
    ioc_2.run();
  });
  t.detach();

  // allow acceptor to accept
  std::this_thread::sleep_for(std::chrono::milliseconds(200));

  tcp::resolver resolver_(ioc_1);
  boost::asio::io_context::strand strand_(ioc_1);
  auto const results = resolver_.resolve("127.0.0.1", "30000");
  boost::asio::connect(ws_1.next_layer(), results.begin(), results.end());
  ws_1.handshake("127.0.0.1", "/");

  ws_1.async_read(buffer, strand_.wrap(on_read));
  strand_.post(writeFromCounterpart);
  auto handlers = ioc_1.run_for(std::chrono::milliseconds(5000));
  std::cout << "Run " + std::to_string(handlers) + " Handlers" << std::endl << std::flush;
}
  • Maybe you can make your sample code self-contained so we can actually see what you are talking about. You might drop Beast and just use `asio::ip::tcp::socket` sockets to simplify. – sehe Nov 28 '18 at 13:30

1 Answers1

1

The short answer is: yes.

The documentation for it is here:

The problem is that sometimes when I run this flow, on::read is not called, so basically I think that the SendSomethingOnTheWs is called before the ws_ was in "reading" state.

Why would that matter? Streaming sockets (ws or otherwise) behave like streams. If the socket was open the data will just be buffered.

sehe
  • 374,641
  • 47
  • 450
  • 633
  • I am not sure I understand, and also, I am not really familiar with websockets: so you're saying that if one endpoint of the socket is writing while the other is not yet reading, the message will be buffered so once the counter part is reading it will still get it? In my case it's not happening, sometimes the message is just lost. If I add the sleep, it's never lost. Also, an important remark: both the "server" (an upgraded ws from a boost::beast http session) and the client counterpart (boost::beast::websocket) run in the same process, on a localhost websocket. I'll add complete code soon. – Liviu Stancu Nov 28 '18 at 13:58
  • Like I said, to make a convincing question, you should add a SSCCE (http://sscce.org/) or MVCE (https://stackoverflow.com/help/mcve). I'll keep an eye on the question until then. Good luck – sehe Nov 28 '18 at 13:59