Search code examples
c++socketsboosttcpboost-asio

moving Boost asio TCP stream


I'm creating a server using boost::asio::ip::tcp, but I'm having problems using streams to do this.

The design pattern I'm using is:

  1. Server initializes a boost::asio::ip::tcp::acceptor, and a boost::asio::ip::tcp::iostream.
  2. Server listens to port with a boost::asio::ip::tcp::acceptor, using async_accept, accepting to the stream object.
  3. When an incomming connection occurs, a new Session object is created. The stream object is passed to the Session, then we repeat step 2 with a new iostream object.

The code looks like this:

class Session; // Ctor: Session(asio::ip::tcp::stream tcp_stream)

class Server
{
public:
    Server(boost::asio::io_service& p_service, unsigned p_port) :
        m_service(p_service),
        m_acc(m_service, boost::asio::ip::tcp::endpoint( asio::ip::tcp::v4(), p_port ) )
    {
        m_acc.async_accept(
                    *m_tcp_stream.rdbuf(),
                    std::bind(&Server::AcceptHandler, this, _1)
                    );
    }

private:
    void AcceptHandler(const boost::system::error_code& p_error)
    {
        if( !p_error )
        {
            boost::asio::ip::tcp::iostream tcp_stream;
            std::swap(m_tcp_stream, tcp_stream);
            new Session( std::move(tcp_stream) );

            m_acc.async_accept(
                        *m_tcp_stream.rdbuf(),
                        std::bind(&Server::AcceptHandler, this, _1)
                        );
        }
    }

private:
    boost::asio::io_service&        m_service;
    boost::asio::ip::tcp::iostream  m_tcp_stream;
    boost::asio::ip::tcp::acceptor  m_acc;
};

My problem is that boost::asio::ip::tcp::iostream has no move ctor. This prevents the std::swap() or new Session() lines from compiling.

I could use this pattern with boost::asio::ip::tcp::socket because it supports the move ctor, but for some reason, streams don't support it. If I could extract a stream from a socket, then I'd be able to work around this, but I can't figure out how to do that.

What's the best way to accept TCP streams and pass the connections to objects which handle sessions?


Solution

  • Just wrap your session in a shared pointer: std::shared_ptr<Session> and keep it prepared for acceptor. Once the connection is instantiated initiate its life-circle. See ASIO example: http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/example/cpp11/echo/async_tcp_echo_server.cpp. There is also session class in it.

    class Server
    {
    public:
        Server(boost::asio::io_service& p_service, unsigned p_port) :
            m_service(p_service),
            m_acc(m_service, boost::asio::ip::tcp::endpoint( asio::ip::tcp::v4(), p_port ) ),
            m_session( std::make_shared< Session >() )
        {
            StartListen();
        }
    
    private:
        void StartListen()
        {
                m_acc.async_accept(
                    m_session->tcp_stream().rdbuf(),
                    std::bind(&Server::AcceptHandler, this, _1) );
        }
    
        void AcceptHandler(const boost::system::error_code& p_error)
        {
            if( !p_error )
            {
                auto ses = std::make_shared< Session >();
                std::swap( ses, m_session );
                ses->InitLifeCircle(); // Start whatever logic you needed.
                StartListen();
            }
        }
    
    private:
        boost::asio::io_service&        m_service;
        boost::asio::ip::tcp::acceptor  m_acc;
        std::shared_ptr< Session >      m_session;
    };
    

    Also you can use the latest boost (1.66). It supports move constructor for boost::asio::ip::tcp::iostream http://www.boost.org/doc/libs/1_66_0/doc/html/boost_asio/reference/ip__tcp/iostream.html.