天天看點

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——1.4 錯誤處理:包裹函數

本節書摘來自異步社群《unix網絡程式設計 卷1:套接字聯網api(第3版)》一書中的第1章,第1.4節,作者:【美】w. richard stevens , bill fenner , andrew m. rudoff著,更多章節内容可以通路雲栖社群“異步社群”公衆号檢視

任何現實世界的程式都必須檢查每個函數調用是否傳回錯誤。在圖1-5所示的程式中,我們檢查socket、inet_pton、connect、read和fputs函數是否傳回錯誤,當發生錯誤時,就調用我們自己的err_quit或err_sys函數輸出一個出錯消息并終止程式的運作。我們發現絕大多數情況下這正是我們想做的事。個别情況下,當這些函數傳回錯誤時,我們想做的事并非簡單地終止程式的運作,如圖5-12所示,我們必須檢查系統調用是否被中斷了。

既然發生錯誤時終止程式的運作是普遍的情況,我們可以通過定義包裹函數(wrapper function)來縮短程式。每個包裹函數完成實際的函數調用,檢查傳回值,并在發生錯誤時終止程序。我們約定包裹函數名是實際函數名的首字母大寫形式。例如,在語句

<code>sockfd = socket(af_inet, sock_stream, 0);</code>

中,函數socket是函數socket的包裹函數,如圖1-7所示。

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——1.4 錯誤處理:包裹函數

在本書中隻要你遇到一個首字母大寫的函數名,它就是我們定義的某個包裹函數。它調用的實際函數的名字與包裹函數名相同,不過以對應的小寫字母開頭。

然而在講解本書中提供的源代碼時,我們總是指稱被調用的最低級别的函數(如socket),而不是包裹函數(如socket)。

這些包裹函數不見得多節省代碼量,但當我們在第26章中讨論線程時,将會發現線程函數遇到錯誤時并不設定标準unix的errno變量,而是把errno的值作為函數傳回值傳回調用者。這意味着每次調用以pthread_開頭的某個函數時,我們必須配置設定一個變量來存放函數傳回值,以便在調用err_sys前把errno變量設定成該值。為避免引入花括号把代碼弄得很混亂,我們可以使用c語言的逗号操作符,把errno的指派與err_sys的調用組合成一條語句,如下所示:

我們也可以為此定義一個新的錯誤處理函數,它取系統的錯誤号作為一個參數,不過通過定義如圖1-8所示的包裹函數,我們可以讓以上這段代碼更為易讀:

《UNIX網絡程式設計 卷1:套接字聯網API(第3版)》——1.4 錯誤處理:包裹函數

<code>pthread_mutex_lock(&amp;ndone_mutex);</code>

要是仔細推敲c代碼的編寫,我們可以用宏來替代函數,進而稍微提高運作時效率,不過包裹函數很少是程式性能的瓶頸所在。

選擇首字母大寫一個函數名作為其包裹函數名是一種折中的方法。其他方法也考慮過,譬如給函數名加一個“e”字首(如[kernighan and pike 1984]一書第182頁所示),給函數名加一個“_e”字尾,等等。這些方法都能明顯地提示調用了其他函數,但我們的這種風格看來是最少分散注意力的。

這種技術還有助于檢查那些錯誤傳回值通常被忽略的函數是否出錯,例如close和listen。

本書後面的例子中,除非必須檢查某個确定的錯誤是否發生,并以不同于終止程序的其他某種方式處理它,否則就使用這些包裹函數。書中不提供所有包裹函數的源代碼,不過它們是可以免費獲得的(見前言)。

unix errno值

隻要一個unix函數(例如某個套接字函數)中有錯誤發生,全局變量errno就被置為一個指明該錯誤類型的正值,函數本身則通常傳回1。err_sys檢視errno變量的值并輸出相應的出錯消息,例如當errno值等于etimedout時,将輸出“connection timed out”(連接配接逾時)。

errno的值隻在函數發生錯誤時設定。如果函數不傳回錯誤,errno的值就沒有定義。errno的所有正數錯誤值都是常值,具有以“e”開頭的全大寫字母名字,并通常在&lt; sys/errno.n &gt;頭檔案中定義。值0不表示任何錯誤。

在全局變量中存放errno值對于共享所有全局變量的多個線程并不适合。我們将在第26章中講述解決這一問題的方法。

全書中我們将使用諸如“connect函數傳回econnrefused”這樣的句子簡明表達以下意思:該函數傳回一個錯誤(通常函數傳回值為-1),同時errno被置為指定的常值。

繼續閱讀