muduo网络库学习之EventLoop(五):TcpConnection生存期管理(连接关闭)

监听套接字可读事件是POLLIN; 已连接套接字正常可读是POLLIN; 正常可写是POLLOUT; 对等方close/shutdown关闭连接,已连接套接字可读是POLLIN | POLLHUP;

时序图分析:

注意:将TcpConnectionPtr 在connections_ 中 erase 掉,时并不会马上 析构TcpConnection 对象(引用计数不为0),

因为此时正处于Channel::handleEvent() 中,如果析构了TcpConnection,那么它的成员channel_ 也会被析构,即导致

core dump.

也就是说TcpConnection 对象生存期要长于handleEvent() 函数,直到执行完connectDestroyed() 后才会析构。

在EventLoop(三)的基础上,在TcpConnection 构造函数中再添加:

// 连接关闭,回调TcpConnection::handleClose
channel_->setCloseCallback(
    boost::bind(&TcpConnection::handleClose, this));
// 发生错误,回调TcpConnection::handleError
channel_->setErrorCallback(
    boost::bind(&TcpConnection::handleError, this));

在 TcpServer::newConnection() 中再添加:

void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{
    .....
    conn->setCloseCallback(
        boost::bind(&TcpServer::removeConnection, this, _1));
}

在TcpConnection::handleRead() 中再添加:

void TcpConnection::handleRead(Timestamp receiveTime)
{
    ssize_t n = ::read(channel_->fd(), buf, sizeof buf);
    if (n > 0)
    {
        messageCallback_(shared_from_this(), buf, n);
    }
    else if (n == 0)
    {
        handleClose();
    }
    else
    {
        errno = savedErrno;
        LOG_SYSERR << "TcpConnection::handleRead";
        handleError();
    }
}

假设现在已经建立了一个新连接,经过几次收发数据后,对等方关闭close套接字,TcpConnection::channel_ 可读事件发生,poll返

回,调用Channel::handleEvent()处理活动通道,调用TcpConnection::handleRead(),::read() 返回0,进而调

用TcpConnection::handleClose()

void TcpConnection::handleClose()
{
    setState(kDisconnected);
    channel_->disableAll();

    TcpConnectionPtr guardThis(shared_from_this());
     connectionCallback_(guardThis);      

    // must be the last line
    closeCallback_(guardThis);  // 调用TcpServer::removeConnection
}

这里需要注意的是有关shared_from_this() 的使用:

class TcpConnection : boost::noncopyable,
    public boost::enable_shared_from_this<TcpConnection>

shared_from_this()  会用当前对象的裸指针构造一个临时智能指针对象,引用计数加1,但马上会被析构,又减1,故无论调用多少

次,对引用计数都没有影响。

TcpConnectionPtr guardThis(shared_from_this()); 为什么不能直接写成TcpConnectionPtr guardThis(this); ?

因为这样写的话,guardThis的引用计数就为1,而不是2,如下例所示:

#include<boost/enable_shared_from_this.hpp>
#include<boost/shared_ptr.hpp>
#include<cassert>

class Y: public boost::enable_shared_from_this<Y>
{
public:
    boost::shared_ptr<Y> f()
    {
        return shared_from_this();
    }

    Y *f2()
    {
        return this;
    }
};

int main(void)
{
    boost::shared_ptr<Y> p(new Y);
    boost::shared_ptr<Y> q = p->f();

    Y *r = p->f2();
    assert(p == q);
    assert(p.get() == r);

    std::cout << p.use_count() << std::endl; //2
    boost::shared_ptr<Y> s(r);
    std::cout << s.use_count() << std::endl; //1
    assert(p == s); //断言失败

    return 0;
}

直接用裸指针生成智能指针对象s后,s的引用计数只是为1,而不会将p引用计数提升为3;如前所述,TcpConnection的生存期就会

成为问题,不能在恰当的时候被释放。

进而调用TcpServer::removeConnection(), 

void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{
    size_t n = connections_.erase(conn->name());

    loop_->queueInLoop(
        boost::bind(&TcpConnection::connectDestroyed, conn));

}

handleEvent() 处理完毕后,当前IO线程继续执行doPendingFunctors() 函数,取出 TcpConnection::connectDestroyed() 执行:

void TcpConnection::connectDestroyed()
{
    loop_->assertInLoopThread();
    if (state_ == kConnected)
    {
        setState(kDisconnected);
        channel_->disableAll();

        connectionCallback_(shared_from_this());
    }
    channel_->remove(); //poll 不再关注此通道
}

参考:

《UNP》

muduo manual.pdf

《linux 多线程服务器编程:使用muduo c++网络库》

本文参与腾讯云自媒体分享计划,欢迎正在阅读的你也加入,一起分享。

发表于

我来说两句

0 条评论
登录 后参与评论

相关文章

来自专栏zcqshine's blog

SpringMVC下获取验证码

5098
来自专栏Java与Android技术栈

RxJava 线程模型分析

RxJava的被观察者在使用操作符时可以利用线程调度器--Scheduler来切换线程,例如

3172
来自专栏开发与安全

muduo网络库学习之muduo_inspect 库涉及到的类

muduo inspect 库通过HTTP方式为服务器提供监控接口, 现在只实现进程相关信息的监控,通过成员ProcessInspector 实现。 Pro...

2075
来自专栏一个会写诗的程序员的博客

第10章 使用 Kotlin 创建 DSL第10章 使用 Kotlin 创建 DSL

使用DSL的编程风格,可以让程序更加简单干净、直观简洁。当然,我们也可以创建自己的 DSL。相对于传统的API, DSL 更加富有表现力、更符合人类语言习惯。

872
来自专栏草根专栏

Rx.NET 简介

官网: http://reactivex.io/ 它支持基本所有的主流语言. 这里我简单介绍一下Rx.NET. 基本概念和RxJS是一样的. 下面开始切入正题....

3659
来自专栏jeremy的技术点滴

《Network Programming with Go》阅读重点备忘(一)

3907
来自专栏Fundebug

JWT究竟是什么呢?

为了保证可读性,本文采用意译而非直译。另外,本文版权归原作者所有,翻译仅用于学习。

1277
来自专栏dotnet & java

jquery调用javascript方法

本来想找个“优雅”一点的方法,类似C#在调用C++方法时候的Invoke之类的。没找到,后来想想,其实也没必要,直接写就好了,算最优雅了吧。只是少了VS的Int...

793
来自专栏*坤的Blog

找BUG

2593
来自专栏xingoo, 一个梦想做发明家的程序员

【手把手教你全文检索】Apache Lucene初探

PS: 苦学一周全文检索,由原来的搜索小白,到初次涉猎,感觉每门技术都博大精深,其中精髓亦是不可一日而语。那小博猪就简单介绍一下这一周的学习历程,仅供各位程...

26610

扫码关注云+社区

领取腾讯云代金券