Eumcoz
Eumcoz

Reputation: 2458

Persistent ASIO connections

I am working on a project where I need to be able to use a few persistent to talk to different servers over long periods of time. This server will have a fairly high throughput. I am having trouble figuring out a way to setup the persistent connections correctly. The best way I could think of to do this is create a persistent connection class. Ideally I would connect to the server one time, and do async_writes as information comes into me. And read information as it comes back to me. I don't think I am structuring my class correctly though.

Here is what I have built right now:

persistent_connection::persistent_connection(std::string ip, std::string port):
    io_service_(), socket_(io_service_), strand_(io_service_), is_setup_(false), outbox_()
{
    boost::asio::ip::tcp::resolver resolver(io_service_);
    boost::asio::ip::tcp::resolver::query query(ip,port);
    boost::asio::ip::tcp::resolver::iterator iterator = resolver.resolve(query);
    boost::asio::ip::tcp::endpoint endpoint = *iterator;
    socket_.async_connect(endpoint, boost::bind(&persistent_connection::handler_connect, this, boost::asio::placeholders::error, iterator));
    io_service_.poll();
}

void persistent_connection::handler_connect(const boost::system::error_code &ec, boost::asio::ip::tcp::resolver::iterator endpoint_iterator)
{
    if(ec)
    {
        std::cout << "Couldn't connect" << ec << std::endl;
        return;
    }
    else
    {
        boost::asio::socket_base::keep_alive option(true);
        socket_.set_option(option);
        boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n", boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
    }
}

void persistent_connection::write(const std::string &message)
{
    write_impl(message);
    //strand_.post(boost::bind(&persistent_connection::write_impl, this, message));
}

void persistent_connection::write_impl(const std::string &message)
{
    outbox_.push_back(message);
    if(outbox_.size() > 1)
    {
        return;
    }
    this->write_to_socket();
}

void persistent_connection::write_to_socket()
{
    std::string message = "GET /"+ outbox_[0] +" HTTP/1.0\r\n";
    message += "Host: 10.1.10.120\r\n";
    message += "Accept: */*\r\n";
    boost::asio::async_write(socket_, boost::asio::buffer(message.c_str(), message.size()), strand_.wrap(
                             boost::bind(&persistent_connection::handle_write, this, boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred)));

}

void persistent_connection::handle_write(const boost::system::error_code& ec, std::size_t bytes_transfered)
{
    outbox_.pop_front();
    if(ec)
    {
        std::cout << "Send error" << boost::system::system_error(ec).what() << std::endl;
    }
    if(!outbox_.empty())
    {
        this->write_to_socket();
    }
    boost::asio::async_read_until(socket_, buf_ ,"\r\n\r\n",boost::bind(&persistent_connection::handle_read_headers, this, boost::asio::placeholders::error));
}

The first message I will send from this seems to send out fine, the server gets it, and responds with a valid response. I see two problem unfortunately:

1) My handle_write is never called after doing the async_write command, I have no clue why. 2) The program never reads the response, I am guessing this is related to #1, since asyn_read_until is not called until that function happens. 3) I was also wondering if someone could tell me why my commented out strand_.post call would not work.

I am guessing most of this has to due with my lack of knowledge of how I should be using my io_service, so if somebody could give me any pointer that would be greatly appreciated. And if you need any additional information, I would be glad to provide some more.

Thank you

Edit call to write:

int main()
{
    persistent_connection p("10.1.10.220", "80");
    p.write("100");
    p.write("200");
    barrier b(1,30000); //Timed mutex, waits for 300 seconds.
    b.wait();
}

and

void persistent_connection::handle_read_headers(const boost::system::error_code &ec)
{
    std::istream is(&buf_);
    std::string read_stuff;
    std::getline(is,read_stuff);
    std::cout << read_stuff << std::endl;
}

Upvotes: 2

Views: 2209

Answers (1)

Tanner Sansbury
Tanner Sansbury

Reputation: 51891

The behavior described is the result of the io_service_'s event loop no longer being processed.

The constructor invokes io_service::poll() which will run handlers that are ready to run and will not block waiting for work to finish, where as io_service::run() will block until all work has finished. Thus, when polling, if the other side of the connection has not written any data, then no handlers may be ready to run, and execution will return from poll().

With regards to threading, if each connection will have its own thread, and the communication is a half-duplex protocol, such as HTTP, then the application code may be simpler if it is written synchronously. On the other hand, if it each connection will have its own thread, but the code is written asynchronously, then consider handling exceptions being thrown from within the event loop. It may be worth reading Boost.Asio's effect of exceptions thrown from handlers.

Also, persistent_connection::write_to_socket() introduces undefined behavior. When invoking boost::asio::async_write(), it is documented that the caller retains ownership of the buffer and must guarantee that the buffer remains valid until the handler is called. In this case, the message buffer is an automatic variable, whose lifespan may end before the persistent_connection::handle_write handler is invoked. One solution could be to change the lifespan of message to match that of persistent_connection by making it a member variable.

Upvotes: 3

Related Questions