Muduo之TcpConnection源码分析笔记
上一节中我们分析到当TcpServer(Acceptor)检测到读事件时,就会创建一个TcpConnection对象,那么这里我们就分析下TcpConnection的细节。
首先我们看下TcpConnection的类数据成员:
以及如下是TcpConnection的构造函数:(PS:其实我们假象认为TcpConnection就是一个socket和一大堆回调函数的集合,代表一个和对端的TCP连接,而TcpConnection里面自然包含了Channel和EventLoop对象)
TcpConnection::TcpConnection(EventLoop* loop,
const string& nameArg,
int sockfd,
const InetAddress& localAddr,
const InetAddress& peerAddr)
: loop_(CHECK_NOTNULL(loop)),
name_(nameArg),
state_(kConnecting),
reading_(true),
socket_(new Socket(sockfd)),
channel_(new Channel(loop, sockfd)),
localAddr_(localAddr),
peerAddr_(peerAddr),
highWaterMark_(64*1024*1024)
{
channel_->setReadCallback(
boost::bind(&TcpConnection::handleRead, this, _1));
channel_->setWriteCallback(
boost::bind(&TcpConnection::handleWrite, this));
channel_->setCloseCallback(
boost::bind(&TcpConnection::handleClose, this));
channel_->setErrorCallback(
boost::bind(&TcpConnection::handleError, this));
LOG_DEBUG << "TcpConnection::ctor[" << name_ << "] at " << this
<< " fd=" << sockfd;
socket_->setKeepAlive(true);
}
从上面的类图可以大概的知道TcpConnection拥有的数据成员,图中省略了一些public接口。
因为TcpConnection是当有连接请求的时候创建的,那么就应该有相应的socket fd。
而在构造函数中我们可以看到创建了Channel对象,之前我们说过Channel是对socket以及回调函数的封装,Acceptor里面都有channel,那么针对TcpConnection也应该有该socketfd以及回调函数的封装,初始化Channel的过程就是将socket和looper关联的过程。另外构造函数中前几句都是对于channel对象中回调函数的一些初始化。那么当TcpConnection创建完成之后又怎么样了呢?我们看到在TcpServer::newConnection()里面最后有这么一句话:
ioLoop->runInLoop(boost::bind(&TcpConnection::connectEstablished, conn));
我们看到这里将新建的TcpConnection加入到了选区的EventLoop任务队列里面。我们看下EventLoop::runInLoop()的代码可以知道这里执行上就是调用了TcpConnection::connectEstablished()。我们来看下它的代码:
void TcpConnection::connectEstablished()
{
loop_->assertInLoopThread();
assert(state_ == kConnecting);
setState(kConnected);
channel_->tie(shared_from_this());
channel_->enableReading();
connectionCallback_(shared_from_this());
}
因为连接这时候是处于accept()之后的因此状态是kConnected,我们可以看到channel->enableReading()
。而这句代码就是将该Channel加入到EventLoop当中去的。 这个因为涉及到Channel的方法我们在之后的内容里面讲解。
总结
当TcpServer::newConnection()创建了TcpConnection之后并调用EventLoop::runInLoop()之后就将该Channel加入到了EventLoop当中去了。之后所有该连接的操作都是由该EventLoop所属的线程处理,Acceptor不再控制,它又回到之前的poll的地方静静地等待下一个连接的到来。