Boost.Asio async_write/async_read - programs do not communicate properly - boost

Good day.
I'm trying to implement a question - answer logic using boost::asio.
On the Client I have:
void Send_Message()
{
....
boost::asio::async_write(server_socket, boost::asio::buffer(&Message, sizeof(Message)), boost::bind(&Client::Handle_Write_Message, this, boost::asio::placeholders::error));
....
}
void Handle_Write_Message(const boost::system::error_code& error)
{
....
std::cout << "Message was sent.\n";
....
boost::asio::async_read(server_socket_,boost::asio::buffer(&Message, sizeof(Message)), boost::bind(&Client::Handle_Read_Message, this, boost::asio::placeholders::error));
....
}
void Handle_Read_Message(const boost::system::error_code& error)
{
....
std::cout << "I have a new message.\n";
....
}
And on the Server i have the "same - logic" code:
void Read_Message()
{
....
boost::asio::async_read(client_socket, boost::asio::buffer(&Message, sizeof(Message)), boost::bind(&Server::Handle_Read_Message, this, boost::asio::placeholders::error));
....
}
void Handle_Read_Message(const boost::system::error_code& error)
{
....
std::cout << "I have a new message.\n";
....
boost::asio::async_write(client_socket_,boost::asio::buffer(&Message, sizeof(Message)), boost::bind(&Server::Handle_Write_Message, this, boost::asio::placeholders::error));
....
}
void Handle_Write_Message(const boost::system::error_code& error)
{
....
std::cout << "Message was sent back.\n";
....
}
Message it's just a structure.
And the output on the Client is: Message was sent.
Output on the Server is: I have a new message.
And that's all. After this both programs are still working but nothing happens.
I tried to implement code like:
if (!error)
{
....
}
else
{
// close sockets and etc.
}
But there are no errors in reading or writing. Both programs are just running normally, but doesn't interact with each other.
This code is quite obvious but i can't understand why it's not working.
Thanks in advance for any advice.

Is the underlying socket a unix or inet socket?
You might want to try a tool like tcpdump or wireshark to watch the exchange between the client and server.

I always forget to call run() or poll() on my boost::asio::io_service. Are you calling that? Because boost requires that called periodically in order to send/receive.

Related

How to Only Catch non-ApolloError Errors with Apollo GraphQL Server

I have an Apollo GraphQL server, where I want to only report internal server errors (not errors extending ApolloError like AuthenticationError, UserInputError, etc.).
Here is the plugin that I wrote that catches internal server errors and reports them:
const errorReportingPlugin = {
requestDidStart(_) {
return {
didEncounterErrors(ctx) {
// If we couldn't parse the operation, don't do anything
if (!ctx.operation) return
for (const err of ctx.errors) {
// Don't report errors extending ApolloError like AuthenticationError, UserInputError, etc.
if (err instanceof ApolloError) {
continue
}
// report error here...
}
}
}
}
}
However err instanceof ApolloError returns false when I throw AuthenticationError, which extends ApolloError.
So I tried to check the class of the err by printing the constructor name and I got GraphQLError.
console.log(err.constructor.name)
Does anyone know how to avoid reporting all errors extending ApolloError?
The solution is to check whether err.originalError (not err) is an instance of ApolloError like this:
if (err.originalError instanceof ApolloError) {
// don't report error since it is a user facing error like AuthenticationError, UserInputError, etc.
}
credit to #xadm

ZeroMQ server client start sequence

Facing a issue if the client is started before server
Specs : ubuntu 16.04 with c++11,libzmq : 4.2.3
problem : sample codes
server.cpp
int main()
{
zmq::context_t context(1);
zmq::socket_t requester(context,ZMQ_ROUTER);
.
//code to get address
.
requester.bind(address);
while(true)
{
zmq::message_t message;
requester.recv(&message);
.
//remaining code
.
}
return 0;
}
client.cpp
int main()
{
zmq::context_t context(1);
zmq::socket_t requester(context,ZMQ_DEALER);
.
//code to get address
.
requester.connect(address);
zmq::message_t message;
.
//populate the message to send
.
requester.send(message);
return 0;
}
I know that in zmq i can start client even if the server is not running,but my client application has to include a safety check which requires the server to be started.Is there any way i can achieve by making connect fail or someother workaround.Timeout options doesnt work for me
Make the send non blocking, if there there is no server(s) available the send will fail and set an errno
http://api.zeromq.org/4-2:zmq-send
ZMQ_DONTWAIT
For socket types (DEALER, PUSH) that block when there are no available peers
(or all peers have full high-water mark), specifies that the operation should
be performed in non-blocking mode. If the message cannot be queued on the
socket, the zmq_send() function shall fail with errno set to EAGAIN.

ZMQ - forwarding request between sockets or one-time proxy

I'm struggling with connecting two sockets:
frontend (ROUTER) - which handles clients request and forward them to backend
backend (ROUTER) - which receives request from frontend and deals with them with the use of number of workers ( which require some initialization, configuration etc).
The server code looks like this:
void server_task::run() {
frontend.bind("tcp://*:5570");
backend.bind("inproc://backend");
zmq::pollitem_t items[] = {
{ frontend, 0, ZMQ_POLLIN, 0 },
{ backend, 0, ZMQ_POLLIN, 0}
};
try {
while (true) {
zmq::poll(&items[0], 2, -1);
if (items[0].revents & ZMQ_POLLIN) {
frontend_h();
}
if (items[1].revents & ZMQ_POLLIN) {
backend_h();
}
}
}
catch (std::exception& e) {
LOG(info) << e.what();
}
}
frontend_h and backend_h are handler classes, each having access to both sockets.
The question is:
Considering synchronous execution of frontend_h() and backend_h() how can I forward the request dealt in frontend_h() to backend_h()?
I tried to simply re-send the message using backend socket like that:
void frontend_handler::handle_query(std::unique_ptr<zmq::message_t> identity, std::unique_ptr<zmq::message_t> request) {
zmq::message_t req_msg, req_identity;
req_msg.copy(request.get());
req_identity.copy(identity.get());
zmq::message_t header = create_header(request_type::REQ_QUERY);
backend.send(header, ZMQ_SNDMORE);
backend.send(message);
}
But it stucks on zmq::poll in run() after the execution of handle_query().
Stucks on zmq::poll()?
Your code has instructed the .poll() method to block, exactly as documentation states:
If the value of timeout is -1, zmq_poll() shall block indefinitely until a requested event has occurred...
How can I forward the request?
It seems pretty expensive to re-marshall each message ( +1 for using at least the .copy() method and avoiding re-packing overheads ) once your code is co-located and the first, receiving handler, can request and invoke any appropriate method of the latter directly ( and without any Context()-processing associated efforts and overheads.

boost asio - exclusive binding to the network port

I develop server app using boost asio. App works great. What doesn't work, is the the exclusive binding to the network port.
I launch one instance of the app - it starts listening to incoming connections.
I launch one more instance - it also starts listening to incoming connections on the same port. Handler that passed to async_accept do not invoked with error as expected.
Usually I just try to acquire the port. If operation fails - port is in use. With Asio this approach does not work. How to check availability of the port?
void TcpServerFactory::acceptConnectionsOnPort(int serverPort,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
// todo check is port not busy
FORMATTED_LOG(this->_log, info) << "Start to accept connections on port " << serverPort;
auto endpoint = boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(), serverPort);
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService, endpoint));
this->acceptConnections(tcpAcceptor, tcpConfig, onSuccessfullyConnectedHandler);
}
void TcpServerFactory::acceptConnections(boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
boost::shared_ptr<boost::asio::ip::tcp::socket> tcpSocket(new boost::asio::ip::tcp::socket(this->_ioService));
boost::function<void(const boost::system::error_code &)> onAcceptOperationCompletedHandler =
boost::bind(&TcpServerFactory::onAcceptOperationCompleted, this->downcasted_shared_from_this<TcpServerFactory>(),
_1, tcpAcceptor, tcpSocket, tcpConfig, onSuccessfullyConnectedHandler);
tcpAcceptor.get()->async_accept(*tcpSocket, onAcceptOperationCompletedHandler);
}
void TcpServerFactory::onAcceptOperationCompleted(const boost::system::error_code & err,
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor,
boost::shared_ptr<boost::asio::ip::tcp::socket> tcpSocket,
boost::shared_ptr<TcpConfigServerReceiver> tcpConfig,
boost::function<void(boost::shared_ptr<TcpServer>)> onSuccessfullyConnectedHandler)
{
if (err)
{
FORMATTED_LOG(this->_log, info) << "Failed to accept connections on port " << tcpAcceptor->local_endpoint().port() << "due to error " << BOOST_ERROR_TO_STREAM(err);
return;
}
this->acceptConnections(tcpAcceptor, tcpConfig, onSuccessfullyConnectedHandler);
this->onConnectionEstablished(tcpSocket, tcpConfig, onSuccessfullyConnectedHandler);
}
Update
I tried to replace constructor of acceptor on series of commands. I expected that on tcpAcceptor->bind() exception will be raised, but that didn't happened.
// boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService, endpoint));
boost::shared_ptr<boost::asio::ip::tcp::acceptor> tcpAcceptor(new boost::asio::ip::tcp::acceptor(this->_ioService));
tcpAcceptor->open(endpoint.protocol());
tcpAcceptor->set_option(boost::asio::socket_base::reuse_address(true));
tcpAcceptor->bind(endpoint);
boost::system::error_code err;
tcpAcceptor->listen(boost::asio::socket_base::max_connections, err);
reuse_address is not supposed to do that. Its meaning to avoid "wait" interval after port was freed.

how to make boost asio async_accept to accept only one single connection?

I'm using the exmple in http://www.boost.org/doc/libs/1_58_0/doc/html/boost_asio/examples/cpp11_examples.html
HTTP server
How to change the example to accept only one single connection at once. That is just accept the next connection when the previous one has finished.
Thanks
In server::do_accept simply do not include the last line (which is to start another async_accept).
void server::do_accept()
{
acceptor_.async_accept(socket_,
[this](boost::system::error_code ec)
{
// Check whether the server was stopped by a signal before this
// completion handler had a chance to run.
if (!acceptor_.is_open())
{
return;
}
if (!ec)
{
connection_manager_.start(std::make_shared<connection>(
std::move(socket_), connection_manager_, request_handler_));
}
// do_accept(); // REMOVE THIS LINE
});
}
As you can see this already used to stop accepting connections on close.
I did with:
if (0 == connection_manager_.size()) {
connection_manager_.start(std::make_shared<connection>(
std::move(socket_), connection_manager_, request_handler_));
} else {
std::move(socket_).close();
}

Resources