2012-04-26 48 views
1

我是C++的新手(來自C)。我從概念上理解RAII應該如何工作,但我無法在其中安裝一個簡單的套接字連接處理程序。如何使用RAII模擬套接字

當前代碼:

void accept_ev(event_handler::token &t, int listenfd) 
{ 
    int newfd = accept(listenfd, NULL, NULL); 

    if (newfd < 0) 
     throw api_server_accept_failed(*this, errno); 

    connections.insert(api_server_connection(newfd)); 
} 

這顯然不是安全的,因爲api_server_connection構造可能分配FD其成員變量之前拋出異常。

所以我的下一個想法是將接受移入構造函數。問題是我真的希望api_server_connection對fd來自何處不可知。例如。如果我想在未來支持inetd,那麼它可能只是作爲fd 0傳入程序。

那麼,我該如何做到這一點。我應該使用不同的構造函數來獲取fd嗎?我是否應該製作子類?另一個選擇可能是有一個lambda函數?

或者我應該只是在這種情況下發現任何錯誤並關閉調用者中的fd?

+3

你看過/閱讀過關於[Boost ASIO](http://www.boost.org/doc/libs/1_49_0/doc/html/boost_asio.html)的工作原理嗎? (你有沒有考慮完全跳過這個,只是使用ASIO?) – 2012-04-26 19:29:40

+0

'api_server_connection'如何引發異常?您沒有向我們顯示該代碼。 – 2012-04-26 19:30:01

+0

@ R.MartinhoFernandes我還沒有寫它......但我知道它會有pimpl,這意味着內存分配,這可能會失敗。 – 2012-04-26 19:43:59

回答

4

現在忽略,套接字,你通常想要做的是將事情分爲兩個階段。

在第一階段,你會做可能會拋出的事情,但如果他們這樣做了,你可以將系統恢復到一個健全的狀態(最好是一個狀態,就好像什麼都沒有發生一樣)。

在第二階段,你做的事情,你可能無法撤消,但你肯定知道永遠不會拋出。要做到這一點,您需要確保可以拋出什麼(特別是)至少一些永遠不會拋棄的特定操作(例如,交換兩個項目)的某些保證。

爲了實現這一點,你通常希望在dtor中恢復到一個健全的狀態,所以如果拋出異常,析構函數會自動清理。

不幸的是,很難說更多的關於你的具體代碼/情況,因爲我們對你正在使用的類不夠了解。

+0

我想你確實讓我以正確的方式思考,所以我想我應該接受這一點。我將創建一個fd_guard類,並首先將fd放入它,但不能失敗。然後我可以無憂地創建連接對象,並讓它繼承fd或fd_guard。 – 2012-04-26 20:10:47

0

首先,爲了使用RAII,您必須以面向對象的方式進行思考。所以我看到你試圖用C++實現服務器類。在這種情況下,您將僅將RAII用於服務器初始化,這意味着您將編寫代碼並在服務器開始偵聽端口時完成它。構造函數中的最後一個函數調用必須是listen或start connections線程。之後,你必須實現第二個線程,它將處理客戶端連接。第二個線程將調用accept來接受客戶端,並在服務器工作時進行迭代。在你的析構函數中,你只需要將你的監聽標誌設置爲false,然後等待接受線程終止後,關閉所有的套接字。