监听套接字可读事件是POLLIN; 已连接套接字正常可读是POLLIN; 正常可写是POLLOUT; 对等方close/shutdown关闭连接,已连接套接字可读是POLLIN | POLLHUP;
时序图分析:
注意:将
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++网络库》