Search code examples
c++boosttcpboost-asio

Why boost async_connect doesn't call my callback?


I am trying to implement a kind of syslog protocol client in boost asio. I succeeded with UDP async version and I am trying to develop TCP version, but stuck. I used WinSyslog and Debian rsyslog as server and Wireshark to control data flow.

I wrote TcpConnection class and prepared test data for it. I use boost asio tcp socket and I expect that packet would be sent using connect and send handlers, but connect handler didn't called.

My Implementation:

class TcpConnection
, public boost::enable_shared_from_this<TcpConnection>
{
public:
    TcpConnection(
        boost::asio::io_service& io_service,
        uint32_t to,
    uint16_t port)    
    : m_socket(io_service)
    , m_ep(boost::asio::ip::address_v4(to), port)
    {
        m_socket.open(boost::asio::ip::tcp::v4());
    }

    void SendMessageAsync(const boost::asio::const_buffer& message) override
    {
    /*
        boost::system::error_code ec;
        m_socket.connect(m_ep, ec);
        m_socket.send(message, 0, ec);*/
  
        m_socket.async_connect(m_ep, 
            boost::bind(&TcpConnection::HandleConnect, shared_from_this(), boost::asio::placeholders::error, message));
    }
 
private:

    void HandleConnect(boost::system::error_code const& ec, const boost::asio::const_buffer& message)
    {
        if (!ec) {
            ec.message();
        }
        else
        {
            m_socket.async_send(message,
                boost::bind(&TcpConnection::HandleWritten, shared_from_this(),
                    boost::asio::placeholders::error, boost::asio::placeholders::bytes_transferred));
        }
    }
    void HandleWritten(boost::system::error_code const& ec, size_t bytes_transferred)
    {   
        if (!ec) {
            EKA_TRACE_ERROR() << TR << ec.message();
    }
}

private:
    boost::asio::ip::tcp::socket m_socket;
    const boost::asio::ip::tcp::endpoint m_ep;
};

int main(int argc, char** argv)
{
    boost::asio::io_service m_io_service;
    auto connection = boost::make_shared<protocoller::syslog::TcpConnection>(
         io_service, asio::ip::address::from_string("10.16.82.22"), 514);

    auto messageStr = "syslog report"
    connection->SendMessageAsync(boost::asio::buffer(boost::asio::buffer(messageStr.data(), messageStr.size())));
    Sleep(5000);
}

I placed breakpoint in HandleConnect and this method never called. I used wireshark and there is successfull connection, but my handler didn't invoked:

wireshark

I tried to find problem with BOOST_ASIO_ENABLE_HANDLER_TRACKING, but there was only @asio|1695732320.140556|0*1|[email protected]_connect

And finally I tried to send by blocking socket and everything work good, so I have a problem only with async sending.


Solution

  • You forget to run() the io_context in your code.

    int main()
    {
        boost::asio::io_service m_io_service;
        auto connection = boost::make_shared<protocoller::syslog::TcpConnection>(
             io_service, asio::ip::address::from_string("10.16.82.22"), 514);
    
        auto messageStr = "syslog report"
        connection->SendMessageAsync(boost::asio::buffer(boost::asio::buffer(messageStr.data(), messageStr.size())));
    
        m_io_service.run_for(std::chrono::milliseconds(5000));
    }
    

    Here's some fixed-up version that works (some conditions were also wrong and the code wasn't self-contained):

    Live On Coliru

    enter image description here

    Note that there are considerable issues with the interfaces and soon with thread-safety, unless you intend to only send one message per connection (which is does look like...?) but then the whole class should be a function.

    Also consider using executors instead of service references.