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的地方靜靜地等待下一個連接的到來。