简体   繁体   中英

Infinite execution of boost asio async_read_until

I faced with periodical infinite waiting for handler call after boost::asio::async_read_until. This appears in two situations:

  • on server, although client side boost::asio::async_write handler was called without any error. This behavour appears only when client sends its first сommand after connect on server.
  • on client, after several minutes of commands exchanging, in this case server side boost::asio::async_write handler called, but the client continues to wait for boost::asio::async_read_until handler.

Given that there is not every time, it seems to me that I using async_read_until, async_write and boost::asio::strand incorrectly.

Here is the simplified server's schema:

  • reading on socket under strand
  • after receiving new command, another async reading started, received command is processed asynchronously in another thread (and without strand)
  • after processing command, result is sent back into the same socket using async_write under strand

(Some commands to server don't required a response, and sometimes it's requred to send a command to the client after some event on server, ie without obtaining data from the client. It is worth to clarify that on the same socket never run simultaneously more than one async_read_until operation and more than one async_write operation.)

And the client's one:

  • after connect send command to the server
  • after receiving command from server, send back an answer

Server's code:

#include <iostream>
#include <istream>
#include <ostream>
#include <string>

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/enable_shared_from_this.hpp>
#include <boost/shared_ptr.hpp>
#include <boost/thread.hpp>

class server_session
    : public boost::enable_shared_from_this<server_session>
{
public:
    server_session(boost::asio::io_service& io_service)
        : _io_service(io_service)
        , _socket(io_service)
        , _strand(io_service)
        , _delimiter('\b')
    {}

    boost::asio::ip::tcp::socket& socket()
    {
        return _socket;
    }

    void read()
    {
        boost::asio::async_read_until(
            _socket,
            _streambuf,
            _delimiter,
            _strand.wrap(
                boost::bind(
                    &server_session::handle_read,
                    shared_from_this(),
                    boost::asio::placeholders::error,
                    boost::asio::placeholders::bytes_transferred
                    )
                )
            );
    }

    void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
    {
        if (ec)
        {
            std::cerr << "async_read_until error: " << ec.message() << std::endl;
            return;
        }

        std::istream is(&_streambuf);
        std::string msg;
        std::getline(is, msg, _delimiter);

        read();

        _io_service.post(
            boost::bind(
                &server_session::proc_msg,
                shared_from_this(),
                msg
                )
            );
    }

    void proc_msg(const std::string msg)
    {
        // command proc here
        std::cout << "received: " << msg << std::endl;

        static std::uint64_t i = 0;
        write("resp_" + std::to_string(i++));
    }

    void write(const std::string& msg)
    {
        _strand.post(
            boost::bind(
                &server_session::write_impl,
                shared_from_this(),
                boost::shared_ptr<std::string>(new std::string(msg + _delimiter))
                )
            );
    }

private:
    void write_impl(const boost::shared_ptr<std::string>& text_ptr)
    {
        boost::asio::async_write(
            _socket,
            boost::asio::buffer(text_ptr->data(), text_ptr->size()),
            _strand.wrap(
                    boost::bind(
                        &server_session::handle_write,
                        shared_from_this(),
                        text_ptr,
                        boost::asio::placeholders::error
                        )
                    )
            );
    }

    void handle_write(const boost::shared_ptr<std::string>, const boost::system::error_code& ec)
    {
        if (ec)
        {            
            std::cerr << "async_write error: " << ec.message() << std::endl;
        }
    }

    boost::asio::io_service& _io_service;
    boost::asio::ip::tcp::socket _socket;
    boost::asio::strand _strand;
    boost::asio::streambuf _streambuf;
    char _delimiter;
};

class server
{
public:
    server(int port)
        : _acceptor(
            _io_service,
            boost::asio::ip::tcp::endpoint(boost::asio::ip::tcp::v4(),port)
            )
    {
        start_accept();

        boost::thread_group thd_grp;

        for (int i = 0; i < 2; ++i)
        {
            thd_grp.create_thread(
                boost::bind(&boost::asio::io_service::run, &_io_service)
                );
        }

        thd_grp.join_all();
    }

private:
    void start_accept()
    {
        _session_ptr.reset(new server_session(_io_service));

        _acceptor.async_accept(
            _session_ptr->socket(),
            boost::bind(
                &server::handle_accept,
                this,
                boost::asio::placeholders::error
                )
            );
    }

    void server::handle_accept(const boost::system::error_code& ec)
    {
        if (ec)
        {
            std::cerr << "handle_accept error: " << ec.message() << std::endl;
            return;
        }

        _session_ptr->read();
        start_accept();
    }

    boost::asio::io_service _io_service;
    boost::asio::ip::tcp::acceptor _acceptor;
    boost::shared_ptr<server_session> _session_ptr;
};

int main(int argc, char** argv)
{
    if (argc != 2)
    {
        std::cerr << "usage: " << argv[0] << " <port>";
        return EXIT_FAILURE;
    }

    server s(std::stoi(argv[1]));
    return EXIT_SUCCESS;
}

Client's code:

#include <iostream>
#include <istream>
#include <ostream>
#include <string>

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

class client
{
public:
    client(const std::string& host, const std::string& port)
        : _socket(_io_service)
        , _resolver(_io_service)
        , _query(host, port)
        , _delimiter('\b')
    {
        _iterator = _resolver.resolve(_query);

        boost::asio::async_connect(
            _socket, 
            _iterator,
            boost::bind(
                &client::handle_connect,
                this,
                boost::asio::placeholders::error
                )
            );

        _io_service.run();
    }

    void handle_connect(const boost::system::error_code& ec)
    {
        if (ec)
        {
            std::cerr << "async_connect error: " << ec.message() << std::endl;
            return;
        }

        write();
    }

    void write()
    {
        static std::uint64_t i = 0;
        boost::shared_ptr<std::string> msg_ptr(new std::string("req_" + std::to_string(i++) + _delimiter));

        boost::asio::async_write(
            _socket,
            boost::asio::buffer(msg_ptr->data(), msg_ptr->size()),
            boost::bind(
                &client::handle_write,
                this,
                msg_ptr,
                boost::asio::placeholders::error
                )
            );
    }

    void handle_write(const boost::shared_ptr<std::string>, const boost::system::error_code& ec)
    {
        if (ec)
        {
            std::cerr << "async_write error: " << ec.message() << std::endl;
            return;
        }

        read();
    }

    void read()
    {
        boost::asio::async_read_until(
            _socket,
            _streambuf,
            _delimiter,
            boost::bind(
                &client::handle_read,
                this,
                boost::asio::placeholders::error,
                boost::asio::placeholders::bytes_transferred
                )
            );
    }

    void handle_read(const boost::system::error_code& ec, std::size_t bytes_transferred)
    {
        if (ec)
        {            
            std::cerr << "async_read_until error: " << ec.message() << std::endl;
            return;
        }

        std::istream is(&_streambuf);
        std::string msg;
        std::getline(is, msg, _delimiter);

        std::cout << "received: " << msg << std::endl;

        write();
    }

    boost::asio::io_service _io_service;     
    boost::asio::ip::tcp::socket _socket;
    boost::asio::ip::tcp::resolver _resolver;
    boost::asio::ip::tcp::resolver::query _query;
    boost::asio::ip::tcp::resolver::iterator _iterator;
    boost::asio::streambuf _streambuf;
    char _delimiter;
};

int main(int argc, char** argv)
{
    if (argc != 3)
    {
        std::cerr << "usage: " << argv[0] << " <host> <port>";
        return EXIT_FAILURE;
    }

    client c(argv[1], argv[2]);
    return EXIT_SUCCESS;
}

Is it correct to async_write while async_read_until on the socket is already running, whereas strand is used?

Is it possible, that async_read_until somehow internally blocks the socket and in fact, data is not sent to the client?

I shall be grateful for any advices why the code may not work properly

I'm using boost 1.58, platforms - Win7 and Win8. Tested on localhost and LAN with the above result.

I can tell you at the very least you should be using asio::deadline_timer and resetting the timer every time you start a new async operation. You should especially be doing this with operations like async_read_until, because you're specifying conditions for the completion of the call which may never, ever be met. Look up the deadline_timer examples and implement an async_wait on every single one of your async ops, specifying a deadline for that operation. In the timeout operation, you can cancel the pending async operations, error out, or restart, whatever is appropriate.

The technical post webpages of this site follow the CC BY-SA 4.0 protocol. If you need to reprint, please indicate the site URL or the original address.Any question please contact:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM