muduo網路庫學習之EventLoop(五):TcpConnection生存期管理(連線關閉)
監聽套接字可讀事件是POLLIN; 已連線套接字正常可讀是POLLIN; 正常可寫是POLLOUT; 對等方close/shutdown關閉連線,已連線套接字可讀是POLLIN | POLLHUP;
時序圖分析:
注意:將TcpConnectionPtr 在connections_ 中 erase 掉,時並不會馬上 析構TcpConnection 物件(引用計數不為0),
因為此時正處於Channel::handleEvent() 中,如果析構了TcpConnection,那麼它的成員channel_
也會被析構,即導致
core dump.
也就是說TcpConnection 物件生存期要長於handleEvent() 函式,直到執行完connectDestroyed() 後才會析構。
在EventLoop(三)的基礎上,在TcpConnection 建構函式中再新增:
C++ Code
1
2 3 4 5 6 |
// 連線關閉,回撥TcpConnection::handleClose
channel_->setCloseCallback( boost::bind(&TcpConnection::handleClose, this)); // 發生錯誤,回撥TcpConnection::handleError channel_->setErrorCallback( boost::bind(&TcpConnection::handleError, this)); |
在 TcpServer::newConnection() 中再新增:
C++ Code
1
2 3 4 5 6 |
void TcpServer::newConnection(int sockfd, const InetAddress &peerAddr)
{ ..... conn->setCloseCallback( boost::bind(&TcpServer::removeConnection, this, _1)); } |
在TcpConnection::handleRead() 中再新增:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 |
void TcpConnection::handleRead(Timestamp receiveTime) { ssize_t n = ::read(channel_->fd(), buf, sizeof buf); if (n > 0) { messageCallback_(shared_from_this(), buf, n); } else if (n == 0) { handleClose(); } else { errno = savedErrno; LOG_SYSERR << "TcpConnection::handleRead"; handleError(); } } |
假設現在已經建立了一個新連線,經過幾次收發資料後,對等方關閉close套接字,TcpConnection::channel_ 可讀事件發生,poll返
回,呼叫Channel::handleEvent()處理活動通道,呼叫TcpConnection::handleRead(),::read()
返回0,進而調
用TcpConnection::handleClose()
C++ Code
1
2 3 4 5 6 7 8 9 10 |
void TcpConnection::handleClose()
{ setState(kDisconnected); channel_->disableAll(); TcpConnectionPtr guardThis(shared_from_this()); connectionCallback_(guardThis);
// must be the last line closeCallback_(guardThis); // 呼叫TcpServer::removeConnection } |
這裡需要注意的是有關shared_from_this() 的使用:
C++ Code
1
2 |
class TcpConnection : boost::noncopyable,
public boost::enable_shared_from_this<TcpConnection> |
shared_from_this() 會用當前物件的裸指標構造一個臨時智慧指標物件,引用計數加1,但馬上會被析構,又減1,故無論呼叫多少
次,對引用計數都沒有影響。
TcpConnectionPtr guardThis(shared_from_this()); 為什麼不能直接寫成TcpConnectionPtr guardThis(this); ?
因為這樣寫的話,guardThis的引用計數就為1,而不是2,如下例所示:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 |
#include<boost/enable_shared_from_this.hpp>
#include<boost/shared_ptr.hpp> #include<cassert> class Y: public boost::enable_shared_from_this<Y> { public: boost::shared_ptr<Y> f() { return shared_from_this(); } Y *f2() { return this; } }; int main(void) { boost::shared_ptr<Y> p(new Y); boost::shared_ptr<Y> q = p->f(); Y *r = p->f2(); assert(p == q); assert(p.get() == r); std::cout << p.use_count() << std::endl; //2 boost::shared_ptr<Y> s(r); std::cout << s.use_count() << std::endl; //1 assert(p == s); //斷言失敗 return 0; } |
直接用裸指標生成智慧指標物件s後,s的引用計數只是為1,而不會將p引用計數提升為3;如前所述,TcpConnection的生存期就會
成為問題,不能在恰當的時候被釋放。
進而呼叫TcpServer::removeConnection(),
C++ Code
1
2 3 4 5 6 7 8 |
void TcpServer::removeConnection(const TcpConnectionPtr &conn)
{ size_t n = connections_.erase(conn->name()); loop_->queueInLoop( boost::bind(&TcpConnection::connectDestroyed, conn)); } |
handleEvent() 處理完畢後,當前IO執行緒繼續執行doPendingFunctors() 函式,取出 TcpConnection::connectDestroyed() 執行:
C++ Code
1
2 3 4 5 6 7 8 9 10 11 12 |
void TcpConnection::connectDestroyed()
{ loop_->assertInLoopThread(); if (state_ == kConnected) { setState(kDisconnected); channel_->disableAll(); connectionCallback_(shared_from_this()); } channel_->remove(); //poll 不再關注此通道 } |
參考:
《UNP》
muduo manual.pdf
《linux 多執行緒伺服器程式設計:使用muduo c++網路庫》
相關文章
- muduo網路庫學習之EventLoop(三):Socket、Acceptor、TcpServer、TcpConnection(連線建立,接收訊息)OOPTCPServer
- muduo網路庫學習之EventLoop(六):TcpConnection::send()、shutdown()、handleRead()、handleWrite()OOPTCP
- muduo網路庫學習之EventLoop(七):TcpClient、ConnectorOOPTCPclient
- muduo網路庫學習筆記(13):TcpConnection生命期的管理筆記TCP
- muduo網路庫學習之EventLoop(四):EventLoopThread 類、EventLoopThreadPool 類OOPthread
- muduo網路庫學習之EventLoop(二):程式(執行緒)wait/notify 和 EventLoop::runInLoopOOP執行緒AI
- muduo網路庫學習筆記(12):TcpServer和TcpConnection類筆記TCPServer
- muduo網路庫學習之EventLoop(一):事件迴圈類圖簡介和muduo 定時器TimeQueueOOP事件定時器
- muduo網路庫學習之muduo_http 庫涉及到的類HTTP
- muduo網路庫學習之muduo_inspect 庫涉及到的類
- 網路學習筆記(一):TCP連線的建立與關閉筆記TCP
- muduo網路庫學習筆記(9):Reactor模式的關鍵結構筆記React模式
- muduo網路庫學習筆記(1):Timestamp類筆記
- muduo網路庫學習筆記(2):原子性操作筆記
- muduo網路庫學習筆記(3):Thread類筆記thread
- muduo網路庫學習筆記(14):chargen服務示例筆記
- muduo網路庫學習筆記(11):有用的runInLoop()函式筆記OOP函式
- 深度學習(五)之原型網路深度學習原型
- muduo網路庫學習之ThreadLocal 類、ThreadLocalSingleton類封裝知識點thread封裝
- muduo網路庫學習筆記(10):定時器的實現筆記定時器
- muduo網路庫學習筆記(7):執行緒特定資料筆記執行緒
- muduo網路庫學習筆記(15):關於使用stdio和iostream的討論筆記iOS
- 菜鳥學網路之 —— 長連線和短連線
- muduo網路庫Timestamp類
- muduo網路庫使用心得
- muduo網路庫學習之Timestamp類、AtomicIntegerT 類封裝中的知識點封裝
- muduo網路庫學習筆記(8):高效日誌類的封裝筆記封裝
- muduo網路庫學習筆記(4):互斥量和條件變數筆記變數
- muduo網路庫學習筆記(5):執行緒池的實現筆記執行緒
- muduo網路庫Exception異常類Exception
- muduo網路庫編譯安裝編譯
- muduo網路庫學習筆記(6):單例類(執行緒安全的)筆記單例執行緒
- muduo網路庫學習之BlockinngQueue類、ThreadPool 類、Singleton類封裝中的知識點BloCthread封裝
- 全連線神經網路學習筆記神經網路筆記
- 學習tcl的幾個好網路連線
- 五分鐘瞭解網路連線
- 滲透測試學習之探測和攻擊無線網路五
- muduo網路庫學習之MutexLock類、MutexLockGuard類、Condition類、CountDownLatch類封裝中的知識點MutexCountDownLatch封裝