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


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

时序图分析:

muduo网络库学习之EventLoop(五):TcpConnection生存期管理(连接关闭)_第1张图片
注意:将 TcpConnectionPtr 在connections_ 中 erase 掉,时并不会马上 析构TcpConnection 对象(引用计数不为0),

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

core dump.

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


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

 C++ Code 
1
2
3
4
5
6
 
// 连接关闭,回调TcpConnection::handleClose
channel_->setCloseCallback(
    boost::bind(&TcpConnection::handleClose,  this));
// 发生错误,回调TcpConnection::handleError
channel_->setErrorCallback(
    boost::bind(&TcpConnection::handleError,  this));

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

 C++ Code 
1
2
3
4
5
6
 
void TcpServer::newConnection( int sockfd,  const InetAddress &peerAddr)
{
    .....
    conn->setCloseCallback(
        boost::bind(&TcpServer::removeConnection,  this, _1));
}

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

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
 

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

 C++ Code 
1
2
3
4
5
6
7
8
9
10
 
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() 的使用:

 C++ Code 
1
2
 
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,如下例所示:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
 
#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(), 

 C++ Code 
1
2
3
4
5
6
7
8
 
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() 执行:

 C++ Code 
1
2
3
4
5
6
7
8
9
10
11
12
 
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++网络库》


你可能感兴趣的:(muduo,tcpConnection)