本文链接:https://blog.csdn.net/Mr_scott_j/article/details/111059068
TcpConnection是muduo中最为复杂的类,其实就是对已连接套接字的一个抽象。
TcpConnection使用Channel来获得socket上的IO事件,它可以自己处理writable事件,而把readable事件通过MessageCallback传达给客户。在TcpConnection析构时候会close(fd)(在Socket析构函数中发生)。
TcpConnection在构造函数中将handleRead注册到Channel::ReadCallback上,并在其中调用messageCallback_;handleWrite由自己处理。
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),//不能为空,否则触发FATAL
name_(nameArg),
state_(kConnecting),
reading_(true),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),//本地地址
peerAddr_(peerAddr),
highWaterMark_(64*1024*1024)
{
//handleRead中会调用messageCallback_
channel_->setReadCallback(
std::bind(&TcpConnection::handleRead, this, _1));
channel_->setWriteCallback(
std::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
std::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
std::bind(&TcpConnection::handleError, this));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true);
}
TcpConnection::CloseCallback是回调给TcpServer和TcpClient,用于通知它们去移除所持有的TcpConnectionPtr,这个接口对普通用户来说是不可见的。注:见newConnection代码31行
TcpConnectionPtr的一连串typedef如下:
typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
typedef std::map<string, TcpConnectionPtr> ConnectionMap;
ConnectionMap connections_;
在TcpServer::newConnection()中,我们把TcpServer::removeConnection()注册到TcpConnection的setCloseCallback上,用于接收连接断开的消息。
在新连接到达时,Acceptor会回调newConnection( ),然后TcpServer创建TcpConnection对象,并加入到Map当中;在连接断开时,TcpConnection会回调removeConnection(),并从Map中移除。
//在构造函数中给acceptor绑定了一个newConnection回调函数,所以当有客户端连接触发accept时,会调用该函数。
void TcpServer::newConnection(int sockfd, const InetAddress& peerAddr)
{
loop_->assertInLoopThread();
//使用round-robin选组一个I/O loop
EventLoop* ioLoop = threadPool_->getNextLoop();
//name_构造
char buf[64];
snprintf(buf, sizeof buf, "-%s#%d", ipPort_.c_str(), nextConnId_);
++nextConnId_;
string connName = name_ + buf;
LOG_INFO << "TcpServer::newConnection [" << name_
<< "] - new connection [" << connName
<< "] from " << peerAddr.toIpPort();
//构造本地地址
InetAddress localAddr(sockets::getLocalAddr(sockfd));
TcpConnectionPtr conn(new TcpConnection(ioLoop,//创建一个连接对象
connName,
sockfd,
localAddr,
peerAddr));
//ConnectionMap std::map<string, TcpConnectionPtr> typedef std::shared_ptr<TcpConnection> TcpConnectionPtr;
connections_[connName] = conn;
//将用户提供的connectionCallback_等原样传给TcpConnection,TcpServer持有TcpConnection的shared_ptr
conn->setConnectionCallback(connectionCallback_);
conn->setMessageCallback(messageCallback_);
conn->setWriteCompleteCallback(writeCompleteCallback_);
//将TcpServer的removeConnection设置到TcpConnection的关闭回调函数中
conn->setCloseCallback(
std::bind(&TcpServer::removeConnection, this, _1)); // FIXME: unsafe
//connectEstablished会调用用户提供的ConnectionCallback
ioLoop->runInLoop(std::bind(&TcpConnection::connectEstablished, conn));
//调用TcpConenction::connectEstablished函数内部会将use_count加一然后减一,此处仍为2
//但是本函数结束后conn对象会析构掉,所以引用计数为1,仅剩connections_列表中存活一个
}
在TcpConnection::connectEstablished()中,我们会回调用户的connectionCallback_,并且加入到Poller中关注。
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread(); //断言处于loop线程
assert(state_ == kConnecting); //断言处于未连接状态
setState(kConnected); //将状态设置为已连接
//之前引用计数为2
channel_->tie(shared_from_this()); //将自身这个TcpConnection对象提升,由于是智能指针,所以不能直接用this
//shared_from_this()之后引用计数+1,为3,但是shared_from_this()是临时对象,析构后又会减一,
//而tie是weak_ptr并不会改变引用计数,所以该函数执行完之后引用计数不会更改
channel_->enableReading(); //一旦连接成功就关注它的可读事件,加入到Poller中关注
connectionCallback_(shared_from_this());
}
enableReading代码如下:
//const int Channel::kReadEvent = POLLIN | POLLPRI;
void enableReading() {
events_ |= kReadEvent; update(); }
回到newConnection函数中来,因为TcpServer的生命周期一般情况下都大于TcpConnection,且~TcpServer()中会对连接进行关闭,因此该函数是安全的。
在此处将原本的一个removeConnection函数拆分成两个『removeConnection()』和『removeConnectionInLoop()』目的在于TcpConnection会在自己的ioLoop线程调用removeConnection(),将其移到TcpServer的loop_线程进行。
void TcpServer::removeConnection(const TcpConnectionPtr& conn)
{
// FIXME: unsafe
//
loop_->runInLoop(std::bind(&TcpServer::removeConnectionInLoop, this, conn));
}
在倒数第三行,将connectDestroyed()移交回TcpConnection的ioLoop,目的是让connectDestroyed()调用的connectionCallback_始终在其ioLoop回调,方便客户端代码编写。
因为将conn从Map中移除,conn的引用计数已经降低为1。将TcpConnection的生命期长到调用connectDestroyed()的时候。
void TcpServer::removeConnectionInLoop(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);
EventLoop* ioLoop = conn->getLoop();
//将TcpConnection的生命期长到调用connectDestroyed的时候,conn为shared_ptr
ioLoop->queueInLoop(
std::bind(&TcpConnection::connectDestroyed, conn));
}
新连接建立时序图:
handleEvent()触发的条件为listening socket可读,表示有新连接到达。TcpServer创建对应的TcpConnection对象。
连接关闭时序图如下:X表示大多数情况下TcpConnection会在此析构