Boost::Asio Async write failed

前端 未结 1 1296
深忆病人
深忆病人 2021-01-21 13:51

I am porting an application which uses Boost::Asio to an embedded system.

I have already cross-compiled boost 1.57.0 binaries for the board using its BSP. To test the l

相关标签:
1条回答
  • 2021-01-21 14:29

    On your async_write_some call you forget to hold a reference to the socket instance.

    This causes the socket object to be destructed, and as part of the destructor, all pending asynchronous operations are canceled. This explains that you receive ec operation_aborted.

    Fix it either by adding the socket pointer to the bound arguments, or alternatively use the enable_shared_from_this idiom with your CSession type.

    Using more shared_pointer magic:

    Here's the "simplest" edit:

    void write_handler(
            boost::shared_ptr<std::string> pstr, 
            boost::shared_ptr<tcp::socket> /*keepalive!*/, 
            error_code ec, size_t bytes_transferred) 
    {
        if(ec)
            std::cout<< "Failed to send! " << boost::system::system_error(ec).what() << "\n";
        else
            std::cout<< *pstr << " has been sent (" << bytes_transferred << " bytes transferred)\n";
    }
    

    Which should be bound like:

        psocket->async_write_some(ba::buffer(*pstr),
                boost::bind(&CService::write_handler, this, pstr, psocket,
                    ba::placeholders::error, ba::placeholders::bytes_transferred));
    

    Live On Coliru

    Several style improvements

    • not using namespace
    • using the asio placeholders (not _1, _2)

    Prints:

    g++ -std=c++11 -O2 -Wall -pedantic main.cpp -pthread -lboost_system -lboost_filesystem && ./a.out& while sleep .1; do nc 127.0.0.1 6767; done
    127.0.0.1
    hello async world!hello async world! has been sent (18 bytes transferred)
    127.0.0.1
    hello async world!hello async world! has been sent (18 bytes transferred)
    127.0.0.1
    hello async world!hello async world! has been sent (18 bytes transferred)
    ...
    

    Using CSession (enable_shared_from_this)

    This is the other idiom, and it avoid spelling out all the shared-pointers.

    Instead of keeping spearate shared pointers to the socket and buffer, you make a class to contain both:

    struct CSession : boost::enable_shared_from_this<CSession> {
        CSession(ba::io_service &iosev)
            :m_iosev(iosev), m_sock(m_iosev)
        {}
    
        void do_response();
    
      private:
        void write_handler(error_code ec, size_t bytes_transferred);
    
        ba::io_service &m_iosev;
        tcp::socket m_sock;
        std::string response;
    };
    

    And now the bind looks like:

    boost::bind(&CSession::write_handler,
         shared_from_this(), /* keep-alive! */
         ba::placeholders::error, ba::placeholders::bytes_transferred)
    

    Much simpler. Session management is the responsibility of the CService, like before:

    void start()
    {
        auto session = boost::make_shared<CSession>(m_iosev);
        m_acceptor.async_accept(session->m_sock,
                boost::bind(&CService::accept_handler, this, session, ba::placeholders::error));
    }
    
    void accept_handler(boost::shared_ptr<CSession> session, error_code ec) {
        if(ec) {
            std::cerr << "Accept failed: " << ec.message() << "\n";
        } else {
            session->do_response();
            start();
        }
    }
    

    Again Live On Coliru

    #include <iostream>
    #include <string>
    #include <boost/asio.hpp>
    #include <boost/bind.hpp>
    #include <boost/make_shared.hpp>
    #include <boost/enable_shared_from_this.hpp>
    
    namespace ba = boost::asio;
    using boost::system::error_code;
    using ba::ip::tcp;
    
    namespace HelloWorld {
    
        struct CSession : boost::enable_shared_from_this<CSession> {
            CSession(ba::io_service &iosev)
                :m_iosev(iosev), m_sock(m_iosev)
            {}
    
            void do_response() {
                response = "hello async world!\n";
                std::cout << m_sock.remote_endpoint().address() << std::endl;
    
                m_sock.async_write_some(ba::buffer(response),
                        boost::bind(&CSession::write_handler,
                            shared_from_this(), /* keep-alive! */
                            ba::placeholders::error, ba::placeholders::bytes_transferred));
            }
    
          private:
    
            void write_handler(error_code ec, size_t bytes_transferred) 
            {
                if(ec)
                    std::cout<< "Failed to send! " << boost::system::system_error(ec).what() << "\n";
                else
                    std::cout<< response << " has been sent (" << bytes_transferred << " bytes transferred)\n";
            }
    
            ba::io_service &m_iosev;
    
            friend class CService;
            tcp::socket m_sock;
    
            std::string response;
        };
    
        struct CService
        {
            CService(ba::io_service &iosev)
                :m_iosev(iosev),m_acceptor(iosev, tcp::endpoint(tcp::v4(), 6767))
            {}
    
            void start() {
                auto session = boost::make_shared<CSession>(m_iosev);
                m_acceptor.async_accept(session->m_sock,
                        boost::bind(&CService::accept_handler, this, session, ba::placeholders::error));
            }
    
            void accept_handler(boost::shared_ptr<CSession> session, error_code ec) {
                if(ec) {
                    std::cerr << "Accept failed: " << ec.message() << "\n";
                } else {
                    session->do_response();
                    start();
                }
            }
    
          private:
            ba::io_service &m_iosev;
            tcp::acceptor m_acceptor;
        };
    }
    
    int main() {
        ba::io_service iosev;
    
        using namespace HelloWorld;
    
        CService sev(iosev);
        sev.start();
        iosev.run();
    }
    

    With similar output.

    0 讨论(0)
提交回复
热议问题