关闭时序¶
TcpServer
¶
当客户端关闭连接时:
面对该连接的TcpConnection对象 Conn
就会触发channel_
的可读事件,进行调用handleEvent()
函数处理。
void Channel::handleEvent(Timestamp receiveTime) {
std::shared_ptr<void> guard;
if (tied_) {
guard = tie_.lock();
if (guard) {
LOG_TRACE << "[6] usecount=" << guard.use_count();
handleEventWithGuard(receiveTime);
LOG_TRACE << "[12] usecount=" << guard.use_count();
}
}
else {
handleEventWithGuard(receiveTime);
}
}
整个客户端关闭,从这里开始,最后也会回到这里。handleEventWithGuard
函数中调用了readCallback_
回调函数。在Conn
的创建时,传入的回调函数是:TcpConnection::handleRead
:
void TcpConnection::handleRead(Timestamp receiveTime) {
loop_->assertInLoopThread();
int savedErrno = 0;
ssize_t n = inputBuffer_.readFd(channel_->fd(), &savedErrno);
if (n > 0) {
messageCallback_(shared_from_this(), &inputBuffer_, receiveTime);
}
else if (n == 0) {
handleClose();
}
else {
errno = savedErrno;
LOG_SYSERR << "TcpConnection::handleRead";
handleError();
}
}
handleClose()
:
void TcpConnection::handleClose() {
loop_->assertInLoopThread();
LOG_TRACE << "fd = " << channel_->fd() << " state = " << stateToString();
assert(state_ == kConnected || state_ == kDisconnecting);
setState(kDisconnected);
channel_->disableAll();
TcpConnectionPtr guardThis(shared_from_this()); // 引用计数加 1
connectionCallback_(guardThis);
closeCallback_(guardThis); // 引用计数减少1
}
handleClose()
函数中,
+ 先调用connectionCallback_(guardThis);
+ 再调用closeCallback_(guardThis);
这两个回调函数都是由TcpServer
在创建TcpConnection
对象时传入,不同的是connectionCallback_(guardThis);
这个函数是由外部传入的,即由TcpServer
的使用者自定义后传入,而closeCallback_(guardThis);
是由TcpServer
内部函数:
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr) {
...
TcpConnectionPtr conn(new TcpConnection(ioLoop, connName, sockfd,
localAddr, peerAddr));
connections_[connName] = conn;
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
conn->setCloseCallback(std::bind(&TcpServer::removeConnection, this, _1));
onn->connectEstablished();
...
}
closeCallback_(guardThis)
时,即执行:
void TcpServer::removeConnection(const TcpConnectionPtr& conn) {
loop_->assertInLoopThread();
LOG_INFO << "TcpServer::removeConnectionInLoop [" << name_
<< "] - connection " << conn->name();
size_t n = connections_.erase(conn->name());
(void)n;
assert(n == 1);
loop_->queueInLoop(
boost::bind(&TcpConnection::connectDestroyed, conn));
}
TcpConnection::connectDestroyed()
,是因为要消除连接,那么就需要注销所有的通道,等这个函数也执行完,就会回到handleEvent()
。对应的连接就会关闭了。
conn 的生命周期¶
TcpConnection
对象是由shared_ptr
管理,因此当最后一个std::shared_ptr
销毁时,那么对应的这个连接对象conn
才会结束其生命周期。可以通过追踪shared_ptr
的引用计数来追踪生命周期。
+ shared_ptr
引用计数增加的方式
+ 通过构造函数初始的引用计数总是1
+ 通过赋值/复制操作,增加引用计数
class Foo {/** ... */};
void fun1(std::shared_ptr<Foo>) {/** ...*/ }
void fun2(std::shared_ptr<Foo>& ) {/** ...*/ }
int main() {
Foo* foo = new Foo;
std::shared_ptr<Foo> sp1(foo);
std::shared_ptr<Foo> sp2(foo);
std::shared_ptr<Foo> sp3 = sp1;
}
=
完成,就和sp1
共享引用,引用计数为2,sp2的引用计数为1。
同理,`fun1`会增加引用计数,而`fun2`不会增加。
conn
的生命周期
由于conn
是通过share_ptr
管理,那么它的生命周期也可通过引用计数来追踪。- 连接建立:
- 建立
conn
时,引用计数为1 connections_[connName] = conn;
引用计数加1conn->connectEstablished();
执行函数时,内部会使得引用计数变为3,执行完恢复2.
cpp shared_from_this() // 创建的是一个临时std::shared_ptr对象
TcpServer::newConnection
执行完毕,创建的局部变量conn
会被销毁,只是剩下一个connections_
中的指向conn
的shared_ptr
对象。
因此,连接建立完成,引用计数为1。
- 建立
- 连接关闭
要想关闭这个连接,那么其引用计数最后必须是0,那么怎么变成0的?Channel::handleEvent
中,guard = tie_.lock();
使得引用计数增加为2.TcpConnectionPtr guardThis(shared_from_this());
创建的guardThis
使得引用计数增加为3,TcpServer::removeConnection(const TcpConnectionPtr& conn)
运行结束引用计数为3.- 传入的是引用并不增加计数
-
connections_.erase(conn->name())
会导致connections_
中的那个shared_ptr
销毁,减少1 -
loop_->queueInLoop(boost::bind(&TcpConnection::connectDestroyed, conn));
,conn
复制传入,增加1。
handleClose()
运行结束,guardThis
是局部变量,会销毁,因此引用计数减少1,变成2.handleEvent()
运行结束,guard
也是局部变量,也会销毁,因此引用计数变成1。- 在
TcpConnection::connectDestroyed()
中:cpp void TcpConnection::connectDestroyed() { loop_->assertInLoopThread(); if (state_ == kConnected) { setState(kDisconnected); channel_->disableAll(); connectionCallback_(shared_from_this()); // 这里 } channel_->remove(); }
当connectionCallback_(shared_from_this());
也执行完毕,连接对象的引用计数就会变成0。到此,conn
生命周期就会结束。
- 连接建立:
-
Channel
的成员
cpp class Channel { private: ... std::weak_ptr<void> tie_; bool tied_; ... };
- 为什么这里的
tie_
数据类型是std::weak_ptr
weak_ptr 是为生命周期设计的如果这里是std::shared_ptr
,那么这个接受的是std::shared_ptr<TcpConnection> tie_
,这就会导致TcpConnection
对象无法释放:即循环引用,因为:TcpConnection
内含一个shared_ptr<Channel>
Channel
又内含一个shared_ptr<TcpConnection>
如此最后都无法释放,那么就会产生内存泄漏。解决办法就是使得其中一个为
std::weak_ptr
。当需要使用所管理的对象时,再使用lock()
函数,返回std::shared_ptr
类型,并且使这个临时对象为局部变量,因此不会造就内存泄漏的问题。 - 为什么这里的