boost asio stateful socket interface - c++

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.

Related

Reacting to each tick events from the websocket

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

calling boost::asio::tcp::socket methods after async_read handler returned an error in server

For log output i am calling tcp::socket::remote_endpoint() from a shared_ptr Session object when the Session is created and when it is destroyed. If an async_read is called and the client has sent a FIN before the server sends a reply and then an RST packet after the server has sent the reply (write doesn't return any errors), the async_read function returns error code system::54 (not_connected - with a message of "Connection reset by peer") and then when i call the remote_endpoint method again (in the Session object destructor) it throws a exception:
libc++abi.dylib: terminating with uncaught exception of type boost::exception_detail::clone_impl<boost::exception_detail::error_info_injector<boost::system::system_error> >: remote_endpoint: Invalid argument
Does the async_read error invalidate the socket or is there another cause of this? I can't see anything in the boost::asio 1.59.0 docs.
I should probably add that this socket is the socket underlying a boost::asio::ssl::stream<tcp::socket&>
An example of the above occurs in this code:
void read() {
auto self(shared_from_this());
boost::asio::async_read(ssl_stream_, boost::asio::buffer(buffer_),
[this, self](const boost::system::error_code &ec, std::size_t) {
if (!ec) {
processBuffer();
} else {
/* system:54 error occurs see here */
std::cout << "read ec: " << ec << " " << ec.message() << std::endl;
/* This will throw an exception (Invalid argument) */
auto endpoint = socket_.remote_endpoint();
}
});
}
Is boost::asio::ssl::stream<tcp::socket&> correct? I think I've only ever seen boost::asio::ssl::stream<tcp::socket> before
Also
I should probably add that this socket is the socket underlying a boost::asio::ssl::stream
You should be async_read-ing from the stream (after handshake). If the stream is in an SSL session, reading/writing from it directly will cause the SSL session to fail, and it might be closed down.

boost asio tcp async read/write

i have an understanding problem how boost asio handles this:
When I watch my request response on client side, I can use following boost example Example
But I don't understand what happens if the server send every X ms some status information to the client. Have I open a serperate socket for this or can my client difference which is the request, response and the cycleMessage ?
Can it happen, that the client send a Request and read is as cycleMessage? Because he is also waiting for async_read because of this Message?
class TcpConnectionServer : public boost::enable_shared_from_this<TcpConnectionServer>
{
public:
typedef boost::shared_ptr<TcpConnectionServer> pointer;
static pointer create(boost::asio::io_service& io_service)
{
return pointer(new TcpConnectionServer(io_service));
}
boost::asio::ip::tcp::socket& socket()
{
return m_socket;
}
void Start()
{
SendCycleMessage();
boost::asio::async_read(
m_socket, boost::asio::buffer(m_data, m_dataSize),
boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
}
private:
TcpConnectionServer(boost::asio::io_service& io_service)
: m_socket(io_service),m_cycleUpdateRate(io_service,boost::posix_time::seconds(1))
{
}
void handle_read_data(const boost::system::error_code& error_code)
{
if (!error_code)
{
std::string answer=doSomeThingWithData(m_data);
writeImpl(answer);
boost::asio::async_read(
m_socket, boost::asio::buffer(m_data, m_dataSize),
boost::bind(&TcpConnectionServer::handle_read_data, shared_from_this(), boost::asio::placeholders::error));
}
else
{
std::cout << error_code.message() << "ERROR DELETE READ \n";
// delete this;
}
}
void SendCycleMessage()
{
std::string data = "some usefull data";
writeImpl(data);
m_cycleUpdateRate.expires_from_now(boost::posix_time::seconds(1));
m_cycleUpdateRate.async_wait(boost::bind(&TcpConnectionServer::SendTracedParameter,this));
}
void writeImpl(const std::string& message)
{
m_messageOutputQueue.push_back(message);
if (m_messageOutputQueue.size() > 1)
{
// outstanding async_write
return;
}
this->write();
}
void write()
{
m_message = m_messageOutputQueue[0];
boost::asio::async_write(
m_socket,
boost::asio::buffer(m_message),
boost::bind(&TcpConnectionServer::writeHandler, this, boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
void writeHandler(const boost::system::error_code& error, const size_t bytesTransferred)
{
m_messageOutputQueue.pop_front();
if (error)
{
std::cerr << "could not write: " << boost::system::system_error(error).what() << std::endl;
return;
}
if (!m_messageOutputQueue.empty())
{
// more messages to send
this->write();
}
}
boost::asio::ip::tcp::socket m_socket;
boost::asio::deadline_timer m_cycleUpdateRate;
std::string m_message;
const size_t m_sizeOfHeader = 5;
boost::array<char, 5> m_headerData;
std::vector<char> m_bodyData;
std::deque<std::string> m_messageOutputQueue;
};
With this implementation I will not need boost::asio::strand or? Because I will not modify the m_messageOutputQueue from an other thread.
But when I have on my client side an m_messageOutputQueue which i can access from an other thread on this point I will need strand? Because then i need the synchronization? Did I understand something wrong?
The differentiation of the message is part of your application protocol.
ASIO merely provides transport.
Now, indeed if you want to have a "keepalive" message you will have to design your protocol in such away that the client can distinguish the messages.
The trick is to think of it at a higher level. Don't deal with async_read on the client directly. Instead, make async_read put messages on a queue (or several queues; the status messages could not even go in a queue but supersede a previous non-handled status update, e.g.).
Then code your client against those queues.
A simple thing that is typically done is to introduce message framing and a message type id:
FRAME offset 0: message length(N)
FRAME offset 4: message data
FRAME offset 4+N: message checksum
FRAME offset 4+N+sizeof checksum: sentinel (e.g. 0x00, or a larger unique signature)
The structure there makes the protocol more extensible. It's easy to add encryption/compression without touch all other code. There's built-in error detection etc.

boost::asio - know when the conection has to be shutdown/closed

I implement a protocol (socks) that requires that my server to relay connections coming from the client to the destination.
The way I implement relaying part is by using something like this:
socket_.async_read_some(boost::asio::buffer(dataClient_, 1024),
boost::bind(&ProxySocksSession::HandleClientProxyRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
remoteSock_.async_read_some(boost::asio::buffer(dataRemote_, 1024),
boost::bind(&ProxySocksSession::HandleRemoteProxyRead, this,
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
Of course there is more code - there are the handlers there that relay data coming from socket_ and sending it to remoteSock_ and the other way around (all data coming from remoteSock_ is relayed to
socket_)
I saw the async tcp server echo example (http://www.boost.org/doc/libs/1_38_0/doc/html/boost_asio/example/echo/async_tcp_echo_server.cpp) and there the logic when to shutdown the connection was simply to delete the connection object - delete this - (that closed the communication socket it owned) when a boost::system::error_code was received in the handler.
How am I supposed to handle this case myself ? This time I have data coming on 2 sockets and if I don't shutdown cleanly I might end up closing before all data was transmitted (e.g. socket from client side - socket_ - might close the connection but - remoteSock - could still be trying to send data).
EDIT I have updated my code to a point where if I detect that one of the sockets (remoteSock_ or socket_) read/write handlers reported an boost::system::error_code I do the following in order to shutdown the communication:
void ProxySocksSession::Shutdown()
{
if (!shutdownInProgress_)
{
std::cout << "Shuting down ..." << std::endl;
shutdownInProgress_ = true;
remoteSock_.shutdown((boost::asio::ip::tcp::socket::shutdown_both));
remoteSock_.close();
socket_.shutdown((boost::asio::ip::tcp::socket::shutdown_both));
socket_.close();
parentConnection_.Shutdown();
}
}
The problem is that even if I call shutdown() and close() on the sockets I still receive calls to the socket handlers (these are in the same class, ProxySocksSession). By the time these come my ProxySocksSession instance is already deleted (deletion is done by parentConnection_.Shutdown() from above)
I have managed to come up with a solution that works (doesn't cause the problems described).
I also include bellow the skeleton for the handler functions in order to see the idea:
void ProxySocksSession::Start()
{
socket_.async_read_some(boost::asio::buffer(dataClient_, 1024),
boost::bind(&ProxySocksSession::HandleClientProxyRead, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
remoteSock_.async_read_some(boost::asio::buffer(dataRemote_, 1024),
boost::bind(&ProxySocksSession::HandleRemoteProxyRead, shared_from_this(),
boost::asio::placeholders::error,
boost::asio::placeholders::bytes_transferred));
}
// received data from socks5 client - completion handler
void ProxySocksSession::HandleClientProxyRead(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error && !this->shutdownInProgress_)
{
// relay data coming from client to remote endpoint -> async write to remoteSock_
// async read some more data from socket_
}
else
{
Shutdown();
}
}
//received data from socks5 remote endpoint (socks5 client destination)
void ProxySocksSession::HandleRemoteProxyRead(const boost::system::error_code& error,
size_t bytes_transferred)
{
if (!error && !this->shutdownInProgress_)
{
// relay data coming from remote endpoint to client -> async write to socket__
// async read some more data from remoteSock_
}
else
{
Shutdown();
}
}
void ProxySocksSession::Shutdown()
{
if (!shutdownInProgress_)
{
std::cout << "Shuting down ..." << std::endl;
shutdownInProgress_ = true;
//remoteSock_.close(); -- no need as it is closed automatically as part of parentConnection_ shutdown/deletion
//socket_.close(); -- no need as it is closed automatically as part of parentConnection_ shutdown/deletion
parentConnection_.Shutdown();
}
}
The key here was the fact that I used shared_from_this() when handing to bind the completion handlers. This way I made sure that deletion of ProxySocksSession instance was not done by parentConnection_ instance that had a shared_ptr to ProxySocksSession before all ProxySocksSession handlers were called while sockets were closing down.

How to check if SSL socket gets closed (async)

I've been using boost asio for networking for some time, but never for SSL sockets. Now i'm required to use SSL sockets and they work pretty fine. But i am not able to find out when a sockets get closed (I usually did this as I did with regular sockets - checking the error value when using boost::asio::async_read_until() in the callback function.
Here's some relevant code snippets:
boost::asio::streambuf streambuf;
boost::asio::ssl::context sslctx(io_service, boost::asio::ssl::context::tlsv1);
boost::asio::ssl::stream<boost::asio::ip::tcp::socket> sock(io_service, sslctx);
void DoAsyncRead()
{
boost::asio::async_read_until(sock, streambuf, "\n", MyReadHandler);
}
void MyReadHandler(const boost::system::error_code& error, size_t bytes_transferred)
{
if (error) {
std::cout << "Read error: " << error.message() << std::endl;
} else {
// ...
}
}
The error condition is never true, even if I kill the server, or drop the client connection. How can I track if the connection is closed?
EOS is not an error condition in most APIs. It is a sentinel value returned instead of a byte count, typically zero (Unix) or -1 (Java).