天天看點

boost::asio 連接配接管理11 如何關閉連接配接 shared_ptr管理連接配接對象的生命周期 銷毀連接配接對象的條件 一般情況下不需要手動關閉socket 多線程環境下關閉連接配接導緻crash 異步讀/寫阻塞導緻連接配接對象無法銷毀 優雅的關閉程序

在實際産品運作中,對連接配接管理有了更新的認識,這裡分享一下。

中的Client對象:

boost::asio 連接配接管理11 如何關閉連接配接 shared_ptr管理連接配接對象的生命周期 銷毀連接配接對象的條件 一般情況下不需要手動關閉socket 多線程環境下關閉連接配接導緻crash 異步讀/寫阻塞導緻連接配接對象無法銷毀 優雅的關閉程式

#include "core/connection.h"  

#include <vector>  

using namespace std;  

class Client: public Connection<Client> {  

 public:  

  Client(io_service& s);  

那麼什麼情況下shared_ptr的引用技術會變成0呢?必須滿足下列所有條件:

1. 如果你不再發起任何異步讀/寫操作

因為每一次異步讀/寫操作都會将Client對象自己的this指針包裝成shared_ptr,通過bind交給boost asio架構,此時架構将持有該shared_ptr,直到讀/寫完成,回調我們自己的函數後才會将引用計數器減1。

2. 如果沒有任何其他對象或者容器持有這個shared_ptr。

實際通信程式為了實作伺服器事件通知,我會将所有的Client對象的shared_ptr儲存在一個容器中,比如map。然後定期的檢查這些Client有沒有在資料庫中有事件要釋出,如果有,則調用Client的方法發送資料。也會定期檢查Client對象代表的連接配接上的心跳消息,如果心跳逾時,則将share_ptr從map中移除掉,并停止任何讀/寫的異步操作(也就是上上面第一個條件)

隻有在滿足了這兩個條件的情況下,shared_ptr會自動銷毀Client對象,Client對象内部的成員變量socket也會被銷毀。

是以,我之前用下面的函數關閉socket一般情況下是不需要的

boost::asio 連接配接管理11 如何關閉連接配接 shared_ptr管理連接配接對象的生命周期 銷毀連接配接對象的條件 一般情況下不需要手動關閉socket 多線程環境下關閉連接配接導緻crash 異步讀/寫阻塞導緻連接配接對象無法銷毀 優雅的關閉程式

void CloseSocket() {    

   try {    

     socket.shutdown(tcp::socket::shutdown_both);    

     socket.close();    

   } catch (std::exception& e) {    

     BOOSTER_INFO("Connection") << "thread id: " << this_thread::get_id() << e.what() << endl;    

   }    

 }    

如果要使用CloseSocket,多線程環境下必須小心。比如我碰到過這樣的情況:一個線程檢查到了逾時,然後調用Client::CloseSocket方法,同時另一個線程正在該Client上發起了異步的讀/寫操作,結果程序崩潰了。boost asio不允許這樣做。

如果就堅持不用CloseSocket行麼,我碰到另一種情況,async_write/async_read會阻塞,結果shared_ptr的引用計數不會為0,是以Client對象無法被銷毀。那麼怎麼安全的調用CloseSocket的呢?用strand,下面是代碼:

void Client::ToClose() {  

  strand_.post(bind(&Client::DoClose, shared_from_this()));  

}  

void Client::DoClose() {  

  CloseSocket();  

其他線程就隻需要調用ToClose函數即可。

由于strand_.post的橋梁作用,CloseSocket會在io_service運作的線程池中被保護。就不會出現和async_read/async_write同時被執行的情況。

在實際程式設計中我們可能由于很多原因要關閉連接配接,比如前面說的心跳逾時,也有收到了不正确的資料,或者在asio傳遞出來的網絡錯誤,又或者是收到了關閉程序的信号。

無論何種原因,都可以采用上面的方式進行關閉連接配接。是以原則隻有一個,記住shared_ptr的生命周期即可。

如何在這種情況下優雅的退出程序是個問題,否則退出時程式會crash,留下core檔案。這種情況是:

1. 線程池中運作這io_service對象,進行異步的讀/寫操作

2. 一個線程定期檢查資料庫中的事件消息,并發送給所有遠端終端,同時檢查每個連接配接的心跳逾時時間。

3. 一個全局的map對象儲存了所有Client的shared_ptr。

在以後的文章中會探索。