6.3 客戶機程序2—增加錯誤檢查
我們的第二個客戶機程序將像第一個客戶機程序一樣,但是將修改它們,考慮錯誤出現的可能性。“將錯誤檢查作為讀者的練習”這樣的項目在編程文獻中相當常見,這或許是因為檢查錯誤相當令人討厭。但是,我贊同這種觀點,即MySQL客戶機程序應該測試錯誤條件
並適當地進行回應。由於某種原因,返回狀態值的客戶機庫的調用做這些事情,而且您要承擔忽略它們的後果。您最終還是要試圖捕獲由於沒有錯誤檢查而出現在程序中的錯誤,這些程序的用戶會對程序運行如此不規律感到奇怪。考慮我們的程序,客戶機程序1。如何知道它是否真正連接到服務器上?可以通過查看服務器的日志,找出與運行程序時間相應的Connect和Quit事件:
這條消息表示根本沒有創建連接。不幸的是,客戶機程序1沒有告訴我們出現的這些結果。實際上它不能。它不能實現任何錯誤檢查,所以它甚至不知道自己發生了什麼事。無論如何,當然不一定必須查看日志來尋找是否能連接到服務器!讓我們立刻改正它。在MySQL客戶機庫中返回值的例程基本上以下列兩種方式之一表示成功或失敗:
■ 成功時,值的指針函數返回一個非NULL 指針,失敗時返回NULL(在這裡NULL 的意思是“C NULL 指針”,而不是“MySQLNULL 列值”)。迄今為止,我們使用的客戶機庫的例程mysql_init() 和MySQL_real_connect() 都用返回連接處理程序的指針來表示成功, NULL 表示失敗。
■ 整型數值的函數一般成功返回0,失敗返回非0。不要測試特定的非0值,如- 1。因為當失敗時,並不保證客戶機庫函數返回任何特定的值。有時,您可能會看到像如下的較舊的錯誤地測試返回值的代碼:
這個測試可能工作,也可能不工作。MySQLAPI 不將任何非0錯誤的返回指定為特定的值,而只判斷它(顯然地)是否為0。這個測試應該寫成下面兩段之一:
或如下所示:
這兩個測試是等價的。如果審核MySQL的源代碼,則可以發現,它基本上用第一種形式測試,因為這編寫起來更簡短。
不是每個API 調用都返回值。我們使用的另一個客戶機例程MySQL_close() 就不返回值(它如何失敗?失敗了又如何?無論如何,都要進行連接)。
當客戶機庫調用失敗,並且需要有關失敗的詳細信息時, API 中的兩個調用都是有用的。mysql_error() 返回包括錯誤信息的字符串,而mysql_errno() 返回數值代碼。應該在錯誤出現以後立刻調用它們,因為如果發布另一個返回狀態的API 調用,則從mysql_error() 或MySQL_errno() 獲取的任何錯誤信息都將來自於後面的調用。
一般來說,程序的用戶查看錯誤字符串比查看錯誤代碼更有啟發。如果只報告兩者中的一個,則建議報告字符串。出於全面考慮,本章的這個樣例報告兩個值。考慮前述的討論,我們將編寫第二個客戶機程序,即客戶機程序2。它類似於客戶機程序
1,但是適當地增加了錯誤檢查代碼。源文件clIEnt2.c 如下所示:
這個錯誤檢查的邏輯是,如果失敗,則mysql_init() 和mysql_real_connect() 都返回NULL。請注意,盡管這個程序檢查mysql_init() 返回的值,但是,如果它失敗,卻不調用錯誤報告函數。這是因為當MySQL_init() 失敗時,不能假設連接處理程序包括任何有意義的信息。
相反,如果MySQL_real_connect() 失敗了,則連接處理程序並不反映有效的連接,但是的確包括傳送給錯誤報告函數的錯誤信息(不要將該處理程序傳送給任何其他的客戶機例程!因為它們一般假設是一個有效連接,所以您的程序可能崩潰)。編譯和連接客戶機程序2,然後試著運行它:
% clIEnt2
如果客戶機程序2沒有別輸出,則連接成功。另一方面,可能會如下所示:
這個輸出表示沒有創建連接,並說明為什麼。或者,它還表示我們的第一個程序,即客戶機程序1,沒有成功地連接到服務器(畢竟客戶機程序1使用同樣的連接參數)!而在那時我們不知道,因為客戶機程序1沒有錯誤檢查。而客戶機程序2做檢查,所以當出問題時,它可以告知我們。這就是應該始終測試API 函數返回值的原因。
MySQL郵件清單問題經常是與錯誤檢查有關的。典型的問題是“當發送這個查詢時,為什麼我的程序崩潰了?”或“我的程序怎麼沒有返回任何東西?”在許多情況下,在查詢發布以前,有疑問的程序不檢查在發布該查詢前是否成功地建立了連接,或者不檢查在試著檢
索結果前確保服務器成功執行該查詢。不要假定每個客戶機庫都調用成功。
本章下面的例子完成錯誤檢查,而且也應該這樣。看起來它好像有更多的工作,但是從長遠地運行來看,它的工作實際上是少的,因為您化費了更少的時間來捕獲錯綜復雜的問題。在第7章“Perl DBI API”和第8章“PHP API”中,也使用這種檢查錯誤的方法。
現在,當運行客戶機2的程序時,假設看到拒絕訪問( Access denIEd)的消息。如何改正這個問題呢?一種可能是將主機名稱、用戶名稱和口令的#define 行更改為允許訪問服務器的值。這是有好處的,在這個意義上,至少應該能做一個連接。但是,這些值是程序中的固定編碼。所以筆者建議不要用這種方法,特別是對口令值。當將自己的程序編譯為二進制格式時,您可能認為口令隱藏起來了,但是,如果有人在程序上運行strings,則它根本隱藏不住(更不用說明讀取訪問源文件的人根本不用做一點工作,就可以獲取口令)。
在“客戶機程序4—運行時獲取連接參數”一節中我們將處理訪問的問題。