上一节中我们分析到当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的地方静静地等待下一个连接的到来。
