Question

I am using Boost-asio to do an IPC between a Java and a C++ application in a Windows environment. I am doing some simple test in the C++ side by now writing data to port 100 in "localhost" server. Here is my code:

int _tmain(int argc, _TCHAR* argv[])
{
    boost::asio::io_service ioService;

    TCPClient client(ioService, "localhost", "100");
    ioService.run();

    client.Write("Heloo!!!");
    getchar();
}

And TCPClient class:

#include <iostream>
#include <stdio.h>
#include <ostream>

#include <boost/bind.hpp>
#include <boost/asio.hpp>

using namespace std;
using boost::asio::ip::tcp;

class TCPClient {
public:
    // Constructor
    TCPClient::TCPClient(boost::asio::io_service& IO_Service, const std::string& server, const std::string& port)
        : resolver_(IO_Service), socket_(IO_Service){
        connected = false;

        // Query donde defino nombre del servidor y puerto
        tcp::resolver::query query(server, port);

        resolver_.async_resolve(query,
            boost::bind(&TCPClient::handle_resolve, this,
            boost::asio::placeholders::error,
            boost::asio::placeholders::iterator));
    }

private:
    tcp::resolver   resolver_;
    tcp::socket     socket_;
    bool connected;

public:
    void Write(const std::string& data){
        size_t s = 8;

        boost::asio::async_write(   socket_,
                                    boost::asio::buffer(data, s),
                                    boost::bind(&TCPClient::handle_write,
                                    this, boost::asio::placeholders::error,
                                    boost::asio::placeholders::bytes_transferred)
                                );
        std::cout << "async_write executed" << std::endl;
    }

private:
    // Resolucion de la direccion del servidor
    void handle_resolve(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator){
        if (!err){
            // Intentamos conectarnos al primer endpoint en la lista
            tcp::endpoint endpoint = *endpoint_iterator;
            socket_.async_connect(  endpoint,
                                    boost::bind(&TCPClient::handle_connect, this,
                                    boost::asio::placeholders::error, ++endpoint_iterator));
        }
        else{
            std::cout << "Error: " << err.message() << std::endl;
        }
    }

    // Conexion al servidor
    void handle_connect(const boost::system::error_code& err, tcp::resolver::iterator endpoint_iterator){
        if (!err){
            connected = true;
            std::cout << "Connected" << std::endl;
        }
        else if (endpoint_iterator != tcp::resolver::iterator()){
            // Conexion fallida, intentemos con el siguiente endpoint
            socket_.close();
            tcp::endpoint endpoint = *endpoint_iterator;

            socket_.async_connect(  endpoint,
                                    boost::bind(&TCPClient::handle_connect, this,
                                    boost::asio::placeholders::error, ++endpoint_iterator));
        }
        else{
            std::cout << "Error: " << err.message() << std::endl;
        }
    }

    void handle_write(const boost::system::error_code& error, std::size_t bytes_transferred){
        if (!error){
            std::cout << "Handle_write() sent bytes" << bytes_transferred << std::endl;
        }
        else {
            std::cout << "Handle_write() error:" << error << std::endl;
        }
    }


};

So I am using a port listener in my Windows machine and I successfully managed to connect to port 100:

enter image description here

But I am not receiving the data Helloo!! and as you can see handle_connect() is not being called. But when i CLOSE the C++ application the data is sent:

enter image description here

What am I missing? I read a lot of examples and I am pretty confused about this library.


EDIT:

Ok this is my new main code:

int _tmain(int argc, _TCHAR* argv[])
{
    boost::asio::io_service ioService;

    TCPClient client(ioService, "localhost", "4000");
    ioService.run();

    client.Write("Heloo!!!");
    ioService.reset();
    ioService.run();
    getchar();
}

I restart the service so the data is sent and now I see handle_write is being called but I still can't see the incoming data until I close the application:

enter image description here

Was it helpful?

Solution

In short, when io_service::run() returns, the io_service has ran out of work. Even though TCPClient::write() adds more work to the io_service, the io_service's event loop is never serviced again, resulting in TCPClient::handle_write() never being invoked. Consider either:

  • Resetting the io_service so that it may be run() again via io_service::reset(), then invoking io_service::run().
  • Restructuring the program so that the io_service never runs out of work by guaranteeing each handler adds additional work to the io_service, or constructing an io_service::work object.

For a deeper understanding of io_service::run(), consider reading this answer.

Additionally, the program invokes undefined behavior as it fails to meet a lifetime requirement for boost::asio::async_write()'s buffers parameter:

[...] ownership of the underlying memory blocks is retained by the caller, which must guarantee that they remain valid until the handler is called.

When client.write("Heloo!!!") is invoked, a temporary std::string is constructed and passed to TCPClient::write(). The temporary string is then used as the buffers argument to async_write(). As TCPClient::write() makes no guarantee that it will not return until the async_write() completion handler has been invoked, the lifetime of the temporary violates a requirement of async_write(), resulting in in undefined behavior.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top