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

轉載請註明出處,本文鏈接:https://www.uj5u.com/qita/233957.html
標籤:其他
上一篇:PS摳圖小技巧-【色彩范圍】
