Reacting to each tick events from the websocket - c++

I would like to design my trading system reacting to each tick events from the websocket stream i subscribed to.
So basically i have two options :
void WebsocketClient::on_write(beast::error_code ec,
std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
ws_.async_read(buffer_,
beast::bind_front_handler(&WebsocketClient::on_message,
shared_from_this()));
}
void WebsocketClient::on_message(beast::error_code ec,
std::size_t bytes_transferred) {
// signal generation and sending http request to place new orders here
// first before calling async_read() below
std::cout << "Received: " << beast::buffers_to_string(buffer_.cdata())
<< std::endl;
ws_.async_read(buffer_,
beast::bind_front_handler(&WebsocketClient::on_message,
shared_from_this()));
}
Or i could
void WebsocketClient::on_message(beast::error_code ec,
std::size_t bytes_transferred) {
ws_.async_read(buffer_,
beast::bind_front_handler(&WebsocketClient::on_message,
shared_from_this()));
// signal generation and sending http request to place new orders here after
// calling async_read()
std::cout << "Received: " << beast::buffers_to_string(buffer_.cdata())
<< std::endl;
}
please give me your suggestions and other ideas i could look at! advance thanks!

There would be no tangible difference, unless
// signal generation and sending http request to place new orders here
// first before calling async_read() below
either
takes significant amount of time (design smell on IO threads)
exits the function by return/exception
That's because async_read by definition always returns immediately

Related

Boost-Beast async web socket Server-Client async read-write not writing output on console

I am trying out Boost Beast examples for asynchronous web socket server - client
I am running server and client as below,
server.exe 127.0.0.1 4242 1
client.exe 127.0.0.1 4242 "Hello"
If everything works I believe it should print "Hello" on server command prompt
Below is the code
void
on_read(
beast::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
// This indicates that the session was closed
if (ec == websocket::error::closed)
return;
if (ec)
fail(ec, "read");
// Echo the message
ws_.text(ws_.got_text());
std::cout << "writing received value " << std::endl;
ws_.async_write(
buffer_.data(),
beast::bind_front_handler(
&session::on_write,
shared_from_this()));
std::cout << buffer_.data().data()<< std::endl;
}
ws_.write() is not writing anything on console , however buffer_data.data() renders
00000163E044EE80
How do I make sure this is working fine? How do I retrieve string value from the socket buffer?
The line printing content of sent message should be placed before async_write:
std::cout << buffer_.data().data()<< std::endl;
ws_.async_write(
buffer_.data(),
beast::bind_front_handler(
&session::on_write,
shared_from_this()));
Why?
All functions from BOOST-ASIO/BEAST which start with async_ ALWAYS return immediately. They initate some task, which are performed in background asio core, and when they are ready, handlers are called.
Look at on_write handler:
void
on_write(
beast::error_code ec,
std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if (ec)
return fail(ec, "write");
// Clear the buffer
buffer_.consume(buffer_.size()); /// <---
consume removes block of bytes whose length is buffer_size from the beginning of buffer_.
Your problem is that buffer probably has been cleared and then it is printed:
thread 1 thread 2
------------------------------ | steps
async_write | | [1]
| consume | [2]
cout << buffer_ | | [3]
|
In addition to using buffer before its consumed, in order to convert buffer I had to write to_string_ function which takes flat buffer and returns the string
std::string to_string_(beast::flat_buffer const& buffer)
{
return std::string(boost::asio::buffer_cast<char const*>(
beast::buffers_front(buffer.data())),
boost::asio::buffer_size(buffer.data()));
};
Found out this can easily be done by beast::buffers_to_string(buffer_.data()) too.
Reference : trying-to-understand-the-boostbeast-multibuffer

Boost::Beast : server with websocket pipelining

I am writing a c++ websocket server with boost beast 1.70 and mysql 8 C connector. The server will have several clients simultaneously connected. The particularity is that each client will perform like 100 websocket requests in a row to the server. Each request is "cpu light" for my server but the server perform a "time heavy" sql request for each request.
I have started my server with the websocket_server_coro.cpp example. The server steps are :
1) a websocket read
2) a sql request
3) a websocket write
The problem is that for a given user, the server is "locked" at the step 2 and cannot read until this step and the step 3 are finished. Thus, the 100 requests are solved sequentially. This is too slow for my use case.
I have read that non blocking read/write are not possible with boost beast. However, what I am trying to do now is to execute async_read and async_write in a coroutine.
void ServerCoro::accept(websocket::stream<beast::tcp_stream> &ws) {
beast::error_code ec;
ws.set_option(websocket::stream_base::timeout::suggested(beast::role_type::server));
ws.set_option(websocket::stream_base::decorator([](websocket::response_type &res) {
res.set(http::field::server, std::string(BOOST_BEAST_VERSION_STRING) + " websocket-Server-coro");
}));
ws.async_accept(yield[ec]);
if (ec) return fail(ec, "accept");
while (!_bStop) {
beast::flat_buffer buffer;
ws.async_read(buffer, yield[ec]);
if (ec == websocket::error::closed) {
std::cout << "=> get closed" << std::endl;
return;
}
if (ec) return fail(ec, "read");
auto buffer_str = new std::string(boost::beast::buffers_to_string(buffer.cdata()));
net::post([&, buffer_str] {
// sql async request such as :
// while (status == (mysql_real_query_nonblocking(this->con, sqlRequest.c_str(), sqlRequest.size()))) {
// ioc.poll_one(ec);
// }
// more sql ...
ws.async_write(net::buffer(worker->getResponse()), yield[ec]); // this line is throwing void boost::coroutines::detail::pull_coroutine_impl<void>::pull(): Assertion `! is_running()' failed.
if (ec) return fail(ec, "write");
});
}
}
The problem is that the line with async_write throw an error :
void boost::coroutines::detail::pull_coroutine_impl::pull(): Assertion `! is_running()' failed.
If a replace this line with a sync_write, it works but the server remains sequential for a given user.
I have tried to execute this code on a single threaded server. I have also tried to use the same strand for async_read and async_write. Still have the assertion error.
Is such server impossible with boost beast for websocket ?
Thank you.
By following the suggestion of Vinnie Falco, I rewrite the code by using "websocket chat" and "async server" as exemple. Here is the final working result of the code :
void Session::on_read(beast::error_code ec, std::size_t bytes_transferred)
{
boost::ignore_unused(bytes_transferred);
if(ec == websocket::error::closed) return; // This indicates that the Session was closed
if(ec) return fail(ec, "read");
net::post([&, that = shared_from_this(), ss = std::make_shared<std::string const>(std::move(boost::beast::buffers_to_string(_buffer.cdata())))] {
/* Sql things that call ioc.poll_one(ec) HERE, for me the sql response go inside worker.getResponse() used below */
net::dispatch(_wsStrand, [&, that = shared_from_this(), sss = std::make_shared < std::string const>(worker.getResponse())] {
async_write(sss);
});
});
_buffer.consume(_buffer.size()); // we remove from the buffer what we just read
do_read(); // go for another read
}
void Session::async_write(const std::shared_ptr<std::string const> &message) {
_writeMessages.push_back(message);
if (_writeMessages.size() > 1) {
BOOST_LOG_TRIVIAL(warning) << "WRITE IS LOCKED";
} else {
_ws.text(_ws.got_text());
_ws.async_write(net::buffer(*_writeMessages.front()), boost::asio::bind_executor(_wsStrand, beast::bind_front_handler(
&Session::on_write, this)));
}
}
void Session::on_write(beast::error_code ec, std::size_t)
{
// Handle the error, if any
if(ec) return fail(ec, "write");
// Remove the string from the queue
_writeMessages.erase(_writeMessages.begin());
// Send the next message if any
if(!_writeMessages.empty())
_ws.async_write(net::buffer(*_writeMessages.front()), boost::asio::bind_executor(_wsStrand, beast::bind_front_handler(
&Session::on_write, this)));
}
Thank you.

How does beast async_read() work & is there an option for it?

I am not very familiar with the boost::asio fundamentals. I am working on a task where I have connected to a web server and reading the response. The response is thrown at a random period, i.e. as and when the response is generated.
For this I am using the boost::beast library which is wrapped over the boost::asio fundamentals.
I have found that the async_read() function is waiting until it is receiving a response.
Now, the thing is : in documentations & example the asynchronous way of websocket communication is shown where the websocket is closed after it receives the response.
that is accomplished by this code (beast docs):
void read_resp(boost::system::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_, std::bind(&session::close_ws_, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
void close_ws_(boost::system::error_code ec, std::size_t bytes_transferred) {
boost::ignore_unused(bytes_transferred);
if(ec)
return fail(ec, "read");
// Close the WebSocket connection
ws_.async_close(websocket::close_code::normal, std::bind(&session::on_close, shared_from_this(), std::placeholders::_1));
}
In this program it is assumed that the sending is completed before receiving. And there is only once response to expect from server. After which, it(client) goes forward and closes the websocket.
But in my program:
I have to check whether the writing part has ended, if not, while the writing is in progress the websocket should come and check the response for whatever is written till now.
Now for this, I have put an if else which will tell my program whether or not my writing is compeleted or not? if not, then the program should go back to the write section and write the required thing. If Yes then go and close the connection.
void write_bytes(//Some parameters) {
//write on websocket
//go to read_resp
}
void read_resp(boost::system::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
if (write_completed){
ws_.async_read(buffer_, std::bind(&session::close_ws_, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
} else {
ws_.async_read(buffer_, std::bind(&session::write_bytes, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
}
void close_ws_(//Some parameters) {
//Same as before
}
Now what I want to do is,After the write is completed wait for 3 seconds and read the websocket after every 1 second, and after 3rd second go to close the websocket.
For that I have included one more if else to check the 3 second condition to the read_resp
void read_resp(boost::system::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
if (write_completed){
if (3_seconds_completed) {
ws_.async_read(buffer_, std::bind(&session::close_ws_, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
} else {
usleep(1000000); //wait for a second
ws_.async_read(buffer_, std::bind(&session::read_resp, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
} else {
ws_.async_read(buffer_, std::bind(&session::write_bytes, shared_from_this(), std::placeholders::_1, std::placeholders::_2));
}
}
But the websocket is waiting for async-read to receive something and in doing so it just goes to session timeout.
how can just just see if something is there to read and then move on to execute the call back?
This might just be an answer for my problem here. I can't guarentee the solution for future readers.
I have removed the read_resp() self-loop and simply let the async_read() to move to close_ws_ when write_completed == true.
The async_read will wait for as long as it receives the response and do not move on to next step thus causing the timeout.

boost::asio::async_receive and 0 bytes in socket

Pseudo-code
boost::asio::streambuf my_buffer;
boost::asio::ip::tcp::socket my_socket;
auto read_handler = [this](const boost::system::error_code& ec, size_t bytes_transferred) {
// my logic
};
my_socket.async_receive(my_buffer.prepare(512),
read_handler);
When using traditional recv with non-blocking socket, it returns -1 when there is nothing to read from socket.
But use of async_receive does not call read_handler if there is no data, and it waits infinitely.
How to realize such a logic (asynchronously) that calls read_handler with bytes_transferred == 0 (possibly with error code set) when there is nothing to read from socket?
(async_read_some has the same behavior).
In short, immediately after initiating the async_receive() operation, cancel it. If the completion handler is invoked with boost::asio::error::operation_aborted as the error, then the operation blocked. Otherwise, the read operation completed with success and has read from the socket or failed for other reasons, such as the remote peer closing the connection.
socket.async_receive(boost::asio::buffer(buffer), handler);
socket.cancel();
Within the initiating function of an asynchronous operation, a non-blocking read will attempt to be made. This behavior is subtlety noted in the async_receive() documentation:
Regardless of whether the asynchronous operation completes immediately or not, [...]
Hence, if the operation completes immediately with success or error, then the completion handler will be ready for invocation and is not cancelable. On the other hand, if the operation would block, then it will be enqueued into the reactor for monitoring, where it becomes cancelable.
One can also obtain similar behavior with synchronous operations by enabling non-blocking mode on the socket. When the socket is set to non-blocking, synchronous operations that would block will instead fail with boost::asio::error::would_block.
socket.non_blocking(true);
auto bytes_transferred = socket.receive(
boost::asio::buffer(buffer), 0 /* flags */, error);
Here is a complete example demonstrating these behaviors:
#include <array>
#include <iostream>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
// This example is not interested in the handlers, so provide a noop function
// that will be passed to bind to meet the handler concept requirements.
void noop() {}
void print_status(
const boost::system::error_code& error,
std::size_t bytes_transferred)
{
std::cout << "error = (" << error << ") " << error.message() << "; "
"bytes_transferred = " << bytes_transferred
<< std::endl;
}
int main()
{
using boost::asio::ip::tcp;
// Create all I/O objects.
boost::asio::io_service io_service;
tcp::acceptor acceptor(io_service, tcp::endpoint(tcp::v4(), 0));
tcp::socket socket1(io_service);
tcp::socket socket2(io_service);
// Connect the sockets.
acceptor.async_accept(socket1, boost::bind(&noop));
socket2.async_connect(acceptor.local_endpoint(), boost::bind(&noop));
io_service.run();
io_service.reset();
std::array<char, 512> buffer;
// Scenario: async_receive when socket has no data.
// Within the intiating asynchronous read function, an attempt to read
// data will be made. If it fails, it will be added to the reactor,
// for monitoring where it can be cancelled.
{
std::cout << "Scenario: async_receive when socket has no data"
<< std::endl;
socket1.async_receive(boost::asio::buffer(buffer), &print_status);
socket1.cancel();
io_service.run();
io_service.reset();
}
// Scenario: async_receive when socket has data.
// The operation will complete within the initiating function, and is
// not available for cancellation.
{
std::cout << "Scenario: async_receive when socket has data" << std::endl;
boost::asio::write(socket2, boost::asio::buffer("hello"));
socket1.async_receive(boost::asio::buffer(buffer), &print_status);
socket1.cancel();
io_service.run();
}
// One can also get the same behavior with synchronous operations by
// enabling non_blocking mode.
boost::system::error_code error;
std::size_t bytes_transferred = 0;
socket1.non_blocking(true);
// Scenario: non-blocking synchronous read when socket has no data.
{
std::cout << "Scenario: non-blocking synchronous read when socket"
" has no data." << std::endl;
bytes_transferred = socket1.receive(
boost::asio::buffer(buffer), 0 /* flags */, error);
assert(error == boost::asio::error::would_block);
print_status(error, bytes_transferred);
}
// Scenario: non-blocking synchronous read when socket has data.
{
std::cout << "Scenario: non-blocking synchronous read when socket"
" has data." << std::endl;
boost::asio::write(socket2, boost::asio::buffer("hello"));
bytes_transferred = socket1.receive(
boost::asio::buffer(buffer), 0 /* flags */, error);
print_status(error, bytes_transferred);
}
}
Output:
Scenario: async_receive when socket has no data
error = (system:125) Operation canceled; bytes_transferred = 0
Scenario: async_receive when socket has data
error = (system:0) Success; bytes_transferred = 6
Scenario: non-blocking synchronous read when socket has no data.
error = (system:11) Resource temporarily unavailable; bytes_transferred = 0
Scenario: non-blocking synchronous read when socket has no data.
error = (system:0) Success; bytes_transferred = 6

boost asio stateful socket interface

I want a Stateful communication but not like boost's echo server example. My socket will be ready for reading forever and whenever it receives a new data it will call a virtual method dataAvailable(string) however it can do async_write anytime.
void connected(const boost::system::error_code &ec) {
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
std::cout << ">> Session::connected()" << std::endl;
}
void handler_read(const boost::system::error_code &ec, size_t bytes_transferred) {
if(ec) {
std::cout << ec.message() << std::endl;
} else {
//std::copy(_buffer, _buffer+bytes_transferred, data.begin());
std::string data(_buffer, _buffer+bytes_transferred);
std::cout << ">> Session[ " << id() << "]" << "::handler_read(): " <<
bytes_transferred << " " << data << std::endl;
boost::asio::async_write(_socket, boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_write, this,
boost::asio::placeholders::error));
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this,
// boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
//call dataAvailable(_buffer);
}
}
void handler_write(const boost::system::error_code &ec) {
if(ec) {
std::cout << ec.message() << std::endl;
} else {
_socket.async_read_some(boost::asio::buffer(_buffer, max_length),
boost::bind(&Session::handler_read, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
//boost::asio::async_read(_socket, boost::asio::buffer(_buffer, max_length),
// boost::bind(&Session::handler_read, this,
// boost::asio::placeholders::error,
// boost::asio::placeholders::bytes_transferred));
}
}
Is this Implementation Okay ? as multiple threads may do read and write operations. where in write operation is updation of some cells in matrix
Why it doesn't work (doesn't echo's the received string) when I use async_read instead of async_read_some
In my listening Server I am nowhere calling the listen method. but still its working. Then Why there is a listen method ? and when it is used ?
I want to get the notification when the client socket is exited from Client side. e.g. Client has closed connection. How can I do it ? I way comming out is by reading an End Of File in read_handler But is that the only Way ?
I've a Session class, and each session have one socket. I am storing Session* colection in a Session Manager. now when I close a socket and delete it that session becomes null. and it may happen in the middle of a vector. So How to safely remove that null session ?
There is one serious issue with your code. Imagine that you have asymmetric network link and are able to receive much faster than send.
Receive message
Do async_write (but it takes time)
Schedule next read
Receive next message
Do second async_write (the first one is not completed yet, you get garbage one other side of link)
Schedule next read
First async_write finishes and schedules one more async_read_some
To sum up, you request asio to do multiple reads and writes to the same socket at once.
Why it doesn't work (doesn't echo's the received string) when I use async_read instead of async_read_some
Are you sending enough data (max_length)? Maybe you would like to use boost::asio::transfer_at_least(min_length)?
In my listening Server I am nowhere calling the listen method. but still its working. Then Why there is a listen method ? and when it is used ?
You probably create acceptor object somewhere.
I want to get the notification when the client socket is exited from Client side. e.g. Client has closed connection. How can I do it ? I way comming out is by reading an End Of File in read_handler But is that the only Way ?
You could create wrapper, which will take EOF and do some cleanup. Then you wrap all your custom handlers with wrapper and pass wrapper as a handler to boost::asio. This way you have only one place to handle EOF error, if you prefer to.
I've a Session class, and each session have one socket. I am storing Session* colection in a Session Manager. now when I close a socket and delete it that session becomes null. and it may happen in the middle of a vector. So How to safely remove that null session ?
Use boost::shared_ptr in handlers and so on and boost::shared_ptr or boost::weak_ptr in Session Manager's vector.