Boost::Asio异步写入失败

Boost::Asio Async write failed

本文关键字:失败 异步 Asio Boost      更新时间:2023-10-16

我正在将一个使用Boost::Asio的应用程序移植到嵌入式系统。

我已经使用它的BSP为板交叉编译了boost 1.57.0二进制文件。为了测试库的工作情况,我运行了两个http服务器示例,分别使用同步和异步编写。

Sync版本运行良好;而异步的写入失败。返回错误"操作已取消"。

有人能指出我应该去哪里吗?谢谢

/*
 * Boost::Asio async example
 */
#include <iostream>
#include <string>
#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/smart_ptr.hpp>
using namespace boost::asio;
using boost::system::error_code;
using ip::tcp;
struct CHelloWorld_Service
{
        CHelloWorld_Service(io_service &iosev)
                :m_iosev(iosev),m_acceptor(iosev, tcp::endpoint(tcp::v4(), 1000))
        {}
        void start()
        {
                boost::shared_ptr<tcp::socket> psocket(new tcp::socket(m_iosev));
                m_acceptor.async_accept(*psocket,
                        boost::bind(&CHelloWorld_Service::accept_handler, this, psocket, _1));
        }
        void accept_handler(boost::shared_ptr<tcp::socket> psocket, error_code ec)
        {
                if(ec) return;
                start();
                std::cout << psocket->remote_endpoint().address() << std::endl;
                boost::shared_ptr<std::string> pstr(new std::string("hello async world!"));
                psocket->async_write_some(buffer(*pstr),
                        boost::bind(&CHelloWorld_Service::write_handler, this, pstr, _1, _2));
        }
        void write_handler(boost::shared_ptr<std::string> pstr, error_code ec,
                size_t bytes_transferred)
        {
                if(ec)
                std::cout<< "Failed to send! " << boost::system::system_error(ec).what() << std::endl;
                else
                std::cout<< *pstr << " has been sent" << std::endl;
        }
        private:
                io_service &m_iosev;
                ip::tcp::acceptor m_acceptor;
};
int main(int argc, char* argv[])
{
        io_service iosev;
        CHelloWorld_Service sev(iosev);
        sev.start();
        iosev.run();
        return 0;
}

async_write_some调用中,您忘记了保存对套接字实例的引用。

这将导致socket对象被析构函数,并且作为析构函数的一部分,所有挂起的异步操作都将被取消。这说明您收到ec operation_aborted

通过将socket指针添加到绑定参数来修复它,或者在CSession类型中使用enable_shared_from_this习惯用法。

使用更多shared_pointer魔术:

以下是"最简单"的编辑:

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";
}

应该像这样绑定:

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

在Coliru上直播

的几种风格改进

  • 不是using namespace
  • 使用asio占位符(不是_1_2(

打印:

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)
...

使用CSession(enable_shared_from_this(

这是另一个习惯用法,它避免拼写出所有共享指针。

您不保留指向套接字和缓冲区的矛状共享指针,而是创建一个同时包含这两个指针的类:

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;
};

现在绑定看起来像:

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

简单多了。会话管理由CService负责,与以前一样:

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();
    }
}

再次在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();
}

输出类似。