boost::asio(2):shared_from_this详解

本文介绍了如何使用智能指针管理动态分配的对象,并探讨了在类中使用enable_shared_from_this时可能出现的问题及正确实践。

这就得从智能指针说起,智能指针就是利用一个变量的构造和析构函数来动态管理一个指针。

说白了就是:构造-->new;析构-->delete


我们可以很容易写一个智能指针:

template <class T>
class TAutoPtr
{

public:
    TAutoPtr() {
        m_t = new T();
        cout << "TAutoPtr::TAutoPtr()" << endl;
    }
    ~TAutoPtr() {
        delete m_t;
        cout << "TAutoPtr::~TAutoPtr()" << endl;
    }

private:
    T*  m_t;
};
使用:

int main(int argc, char** argv)
{
    TAutoPtr<int> tt;
    
    return 0;
}

这没有问题,很OK。


boost::asio就提供了该方法,可以迅速让shared_ptr来管理你的类。

但是这样就导致了一个问题,当你的类派生之enable_shared_from_this的时候,无法在类成员函数里面使用shared_from_this()获取类指针。

如下的用法是错误:

class B { 
public: 
    B(): x_(4) { 
        cout << "B::B()" << endl; 
    } 


    ~B() { 
        cout << "B::~B()" << endl; 
    } 


    void f() { 
        shared_ptr<B> p(this); 
        cout << p->x_ << endl; 
    } 


private: 
    int x_; 
}; 

int main(int argc, char** argv) { 
    shared_ptr<B> x(new B); 
    x->f(); 
    return 0; 
} 

输出为:

B::B() 

B::~B() 
B::~B() 

两次析构同一个对象,发生灾难性后果。


同样,如下的用法也是错误的:

class A : public enable_shared_from_this<A> { 
public: 
    A() { 
        cout << "A::A()" << endl; 
    } 


    ~A() { 
        cout << "A::~A()" << endl; 
    } 


    void f() { 
        //cout << shared_from_this()->x_ << endl; // this way is okay too
        shared_from_this();
        //shared_ptr<A> p = shared_from_this();
        //cout << p->x_ << endl;
    }


private:
    int x_;
};

int main(int argc, char** argv)
{
    A* aa = new A();
    aa->f();
    
    return 0;
}
虽然我们已经将类A派生之enable_shared_from_this,但是我们使用的时候并没有用shared_ptr<A>包装类A。也错误。


总结如下:

1. 将该类派生之enable_shared_from_this。例如:class A : public enable_shared_from_this<A>

2. 使用的时候必须加上shared_ptr<A> abc(new A())


<think>问题分析:在关闭socket后,同一个stream对象被再次用于连接,但该stream对象已经处于关闭状态,导致连接失败。 解决方案:在重新连接前,需要重置stream_对象,即创建一个新的tcp_stream对象,而不是继续使用已经关闭的旧对象。 修改后的代码: 1. 在关闭现有socket后,重置stream_,然后重新创建一个新的tcp_stream对象。 2. 由于使用了std::shared_ptr管理stream_,我们可以通过重新赋值来重置。 注意:在异步操作中,要确保对象的生命周期,尤其是在lambda中捕获shared_ptr时,避免在操作完成前对象被销毁。 具体修改如下:</think>### 问题分析与修复方案 问题根本原因:**在已关闭的socket上尝试重新连接**。当`async_connect`操作因超时被取消后,底层socket处于无效状态,此时直接重用同一个`tcp_stream`对象进行连接会导致错误。 ### 解决方案:重建socket对象 ```cpp #include <boost/beast/core.hpp> #include <boost/asio/ip/tcp.hpp> namespace beast = boost::beast; namespace net = boost::asio; using tcp = net::ip::tcp; class ClientSession : public std::enable_shared_from_this<ClientSession> { public: void CloseSocketAndConnect(int timeout) { // 安全关闭现有socket boost::system::error_code ec; if (stream_) { stream_->cancel(); stream_->socket().shutdown(tcp::socket::shutdown_both, ec); stream_->socket().close(ec); } // 清除缓冲区 buffer_.consume(buffer_.size()); buffer_.clear(); // 关键修复:重建socket对象 stream_.reset(); // 释放旧对象 stream_ = std::make_shared<beast::tcp_stream>(io_context_); // 创建新对象 auto const address = net::ip::make_address(host_); stream_->expires_after(std::chrono::milliseconds(timeout)); // 异步连接(捕获stream_确保生命周期) stream_->async_connect( tcp::endpoint{address, static_cast<unsigned short>(port_)}, [self = shared_from_this(), timeout, s = stream_] (beast::error_code ec) { self->OnConnect(ec, timeout); } ); } private: std::shared_ptr<beast::tcp_stream> stream_; beast::flat_buffer buffer_; net::io_context& io_context_; // 需要传入io_context std::string host_; unsigned short port_; }; ``` ### 关键修复点解释 1. **socket重建机制** ```cpp stream_.reset(); // 释放旧资源 stream_ = std::make_shared<beast::tcp_stream>(io_context_); // 创建新socket ``` - 必须销毁旧socket对象,否则底层socket描述符无效 - 新对象确保使用全新的操作系统级socket描述符 2. **异步操作安全** ```cpp [self = shared_from_this(), timeout, s = stream_](...){...} ``` - `s = stream_`:显式捕获当前stream指针,防止回调时对象被替换 - `self = shared_from_this()`:确保session对象在回调期间存活 3. **错误处理优化** - 检查所有`error_code`,避免异常传播 - 忽略`shutdown`错误(可能已关闭) 4. **资源清理** ```cpp buffer_.consume(buffer_.size()); // 释放内存 buffer_.clear(); // 重置状态 ``` ### 超时处理机制详解 Boost.Beast/Asio的超时控制流程: ```mermaid sequenceDiagram participant App as 应用程序 participant Stream as tcp_stream participant OS as 操作系统 App->>Stream: expires_after(timeout) Stream->>OS: 发起异步连接 alt 连接成功 OS->>Stream: 返回成功 Stream->>App: 调用回调 else 超时发生 Stream->>OS: 取消操作(ECANCELED) Stream->>App: 回调返回operation_aborted end ``` ### 最佳实践建议 1. **连接状态检查** ```cpp if (stream_->socket().is_open()) { // 仅当socket打开时才关闭 } ``` 2. **错误日志记录** ```cpp if (ec) { std::cerr << "Socket error: " << ec.message() << "\n"; } ``` 3. **超时值配置** - 连接超时:3-30秒(根据网络环境调整) - 读写超时:5-60秒(根据数据量调整) 4. **重连策略** ```cpp void OnConnect(beast::error_code ec, int timeout) { if (ec == net::error::operation_aborted) { // 超时重连(带退避算法) auto delay = std::min(timeout * 2, 30000); timer_.expires_after(delay); timer_.async_wait([=](ec){ CloseSocketAndConnect(delay); }); } } ```
评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值