boost::asio的失败尝试

异步服务端写数据没问题,读数据总是失败。网上的异步服务端的例子要么是只有写的,要么是分为两个类的。我这个例子,accept和读写封装在一个类总是失败。

#include <boost/asio.hpp>
#include <boost/bind.hpp>
#include <boost/shared_ptr.hpp>
#include <iostream>
#include <unistd.h>
#include <boost/smart_ptr/scoped_ptr.hpp>
#include <boost/smart_ptr/make_shared_object.hpp>
#include <bits/shared_ptr.h>
#include <boost/smart_ptr/enable_shared_from_this.hpp>

using namespace boost::asio;

class TcpServer : public boost::enable_shared_from_this<TcpServer> {
public:

    TcpServer(int port) : m_iosvc(), m_acpt(m_iosvc, ip::tcp::endpoint(ip::tcp::v4(), port)) {
    }

    void onService() {
        startAccept();
        m_iosvc.run();
    }

private:

    void startAccept() {
        //        ip::tcp::socket* newskt = new ip::tcp::socket(m_iosvc);
        boost::shared_ptr<ip::tcp::socket> newskt = boost::make_shared<ip::tcp::socket>(m_iosvc);
        m_acpt.async_accept(*newskt, boost::bind(&TcpServer::accept_handler, this, newskt, placeholders::error));

    }

    void accept_handler(boost::shared_ptr<ip::tcp::socket> skt, const boost::system::error_code& error) {
        if (error) {
            std::cout << "error: " << error.message() << std::endl;
            m_iosvc.stop();
            return;
        }
        startAccept();

        std::cout << "connection arrived: " << skt->remote_endpoint().address() << std::endl;
        char* readbuf = new char[3];
        boost::shared_ptr<std::string> responseMsg = boost::make_shared<std::string>("ok");

        //        skt->async_write_some(buffer(*responseMsg),
        //                boost::bind(&TcpServer::write_handler, this, responseMsg, placeholders::error, placeholders::bytes_transferred));
        //        async_read(*skt, buffer(readbuf, 2), 
        //                boost::bind(&TcpServer::read_handler, this, readbuf, placeholders::error, placeholders::bytes_transferred));
        skt->async_read_some(buffer(readbuf, 2),
                boost::bind(&TcpServer::read_handler, shared_from_this(), readbuf, placeholders::error, placeholders::bytes_transferred));
    }

    void write_handler(boost::shared_ptr<std::string> pstr,
            boost::system::error_code ec, size_t bytes_transferred) {
        if (ec)
            std::cout << "send fail!" << std::endl;

        else
            std::cout << *pstr << " sent" << std::endl;
    }

    void read_handler(char* readbuf, const boost::system::error_code& error, std::size_t bytes_transferred) {
        if (error.value() != boost::system::errc::operation_canceled) {
            std::cout << "read_handler error: " << error.message() << std::endl;
            m_iosvc.stop();
            return;
        }
        if (bytes_transferred != 2) {
            std::cout << "bytes_transferred: " << bytes_transferred << std::endl;
            m_iosvc.stop();

            return;
        }
        std::cout << "data: " << *readbuf << std::endl;
    }

    io_service m_iosvc;
    ip::tcp::acceptor m_acpt;
};

int main(int argc, char* argv[]) {

    TcpServer server(9527);
    server.onService();

    return 0;
}

用下面中方式,异常为 operation canceled

skt->async_read_some(buffer(readbuf, 2),
                boost::bind(&TcpServer::read_handler, this, readbuf, placeholders::error, placeholders::bytes_transferred));
看到stack overflow上有人说应该把 this 换成 shared_from_this() ,就像我现在代码的写法,错误是 tr1::bad_weak_ptr。

接下来我将像网上的例子那样把这个类拆分为两个类,一个处理accept,一个处理读写。

更新:

经过与网上拆分为两个类的例子对比发现,区别在于startAccept()中创建并交给shared_ptr托管的ip::tcp::socket类型变量newskt的生命周期。

网上拆分为两个类的例子中,在调用async_accept前创建socket类型变量skt,被由负责读写的connection类保管,只有在connection释放即读写完成后skt才会被释放。而我的例子中,socket类型变量由shared_ptr托管,被传入TcpServer::accept_handler中,它将在TcpServer::accept_handler函数返回后被释放。而TcpServer::accept_handler函数内调用async_read,并设置读事件回调TcpServer::read_handler。由此,尽管读事件回调TcpServer::read_handler在我这层代码上并没有直接用到ip::tcp::socket类型变量,但是读事件内部必须依赖socket类型变量,否则就会像我的例子里socket类型变量提前释放,导致异常operation canceled。

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值