本文鏈接: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會在此析構