boost::asio 連接管理11 如何關閉連接


在實際產品運行中,對連接管理有了更新的認識,這里分享一下。

shared_ptr管理連接對象的生命周期

shared_ptr的引用計數器決定了連接對象的生命周期。這里我說的連接對象就是在我的前文:http://blog.csdn.net/csfreebird/article/details/8522620

中的Client對象:

 

[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. #include "core/connection.h"  
  2. #include <vector>  
  3.   
  4. using namespace std;  
  5.   
  6. class Client: public Connection<Client> {  
  7.  public:  
  8.   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

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

 

[cpp]  view plain copy print ? 在CODE上查看代碼片 派生到我的代碼片
 
  1. void CloseSocket() {    
  2.    try {    
  3.      socket.shutdown(tcp::socket::shutdown_both);    
  4.      socket.close();    
  5.    } catch (std::exception& e) {    
  6.      BOOSTER_INFO("Connection") << "thread id: " << this_thread::get_id() << e.what() << endl;    
  7.    }    
  8.  }    

多線程環境下關閉連接導致crash

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

參考:http://web.archiveorange.com/archive/v/Q0J4VefPMc2v8QYvcVr3

 

異步讀/寫阻塞導致連接對象無法銷毀

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

 

[cpp]  view plain copy print ?
 
  1. void Client::ToClose() {  
  2.   strand_.post(bind(&Client::DoClose, shared_from_this()));  
  3. }  
  4.   
  5. void Client::DoClose() {  
  6.   CloseSocket();  
  7. }  

其他線程就只需要調用ToClose函數即可。
由於strand_.post的橋梁作用,CloseSocket會在io_service運行的線程池中被保護。就不會出現和async_read/async_write同時被執行的情況。

 

 

在實際編程中我們可能由於很多原因要關閉連接,比如前面說的心跳超時,也有收到了不正確的數據,或者在asio傳遞出來的網絡錯誤,又或者是收到了關閉進程的信號。

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

 

優雅的關閉進程

 

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

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

2. 一個線程定期檢查數據庫中的事件消息,並發送給所有遠程終端,同時檢查每個連接的心跳超時時間。

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

在以后的文章中會探索。


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM