概述
在當今的網絡時代,下載軟件是使用最為頻繁的軟件之一。幾年來,下載技術也在不停地發展。最原始的下載功能僅僅是個“下載”過程,即從WEB服務器上連續地讀取文件。其最大的問題是,由於網絡的不穩定性,一旦連接斷開使得下載過程中斷,就不得不全部從頭再來一次。
隨後,“斷點續傳”的概念就出來了,顧名思義,就是如果下載中斷,在重新建立連接後,跳過已經下載的部分,而只下載還沒有下載的部分。
無論“多線程下載”技術是否洪以容先生的發明,洪以容使得這項技術得到前所未有的關注是不爭的事實。在“網絡螞蟻”軟件流行開後,許多下載軟件也都紛紛效仿,是否具?quot;多線程下載"技術、甚至能支持多少個下載線程都成了人們評測下載軟件的要素。"多線程下載"的基礎是WEB服務器支持遠程的隨機讀取,也即支持"斷點續傳"。這樣,在下載時可以把文件分成若干部分,每一部分創建一個下載線程進行下載。
現在,不要說編寫專門的下載軟件,在自己編寫的軟件中,加入下載功能有時也非常必要。如讓自己的軟件支持自動在線升級,或者在軟件中自動下載新的數據進行數據更新,這都是很有用、而且很實用的功能。本文的主題即怎樣編寫一個支持"斷點續傳"和"多線程"的下載模塊。當然,下載的過程非常復雜,在一篇文章中難以全部闡明,所以,與下載過程關系不直接的部分基本上都忽略了,如異常處理和網絡錯誤處理等,敬請各位讀者注意。我使用的開發環境是C++ Builder 5.0,使用其他開發環境或者編程語言的朋友請自行作適當修改。
HTTP協議簡介
下載文件是電腦與WEB服務器交互的過程,它們交互的"語言"的專業名稱是協議。傳送文件的協議有多種,最常用的是HTTP(超文本傳輸協議)和FTP(文件傳送協議),我采用的是HTTP。
HTTP協議最基本的命令只有三條:Get、Post和Head。Get從WEB服務器請求一個特定的對象,比如HTML頁面或者一個文件,WEB服務器通過一個Socket連接發送此對象作為響應;Head命令使服務器給出此對象的基本描述,比如對象的類型、大小和更新時間。Post命令用於向WEB服務器發送數據,通常使把信息發送給一個單獨的應用程序,經處理生成動態的結果返回給浏覽器。下載即是通過Get命令實現。
基本的下載過程
編寫下載程序,可以直接使用Socket函數,但是這要求開發人員理解、熟悉TCP/IP協議。為了簡化Internet客戶端軟件的開發,Windows提供了一套WinInet API,對常用的網絡協議進行了封裝,把開發Internet軟件的門檻大大降低了。我們需要使用的WinInet API函數如圖1所示,調用順序基本上是從上到下,其具體的函數原型請參考MSDN。
圖1
在使用這些函數時,必須嚴格區分它們使用的句柄。這些句柄的類型是一樣的,都是HINTERNET,但是作用不同,這一點非常讓人迷惑。按照這些句柄的產生順序和調用關系,可以分為三個級別,下一級的句柄由上一級的句柄得到。
InternetOpen是最先調用的函數,它返回的HINTERNET句柄級別最高,我習慣定義為hSession,即會話句柄。
InternetConnect使用hSession句柄,返回的是http連接句柄,我把它定義為hConnect。
HttpOpenRequest使用hConnect句柄,返回的句柄是http請求句柄,定義為hRequest。
HttpSendRequest、HttpQueryInfo、InternetSetFilePointer和InternetReadFile都使用HttpOpenRequest返回的句柄,即hRequest。
當這幾個句柄不再使用是,應該用函數InternetCloseHandle把它關閉,以釋放其占用的資源。
首先建立一個名為THttpGetThread、創建後自動掛起的線程模塊,我希望線程在完成後自動銷毀,所以在構造函數中設置:
FreeOnTerminate = True; // 自動刪除
並增加以下成員變量:
char Buffer[HTTPGET_BUFFER_MAX+4]; // 數據緩沖區
AnsiString FURL; // 下載對象的URL
AnsiString FOutFileName; // 保存的路徑和名稱
HINTERNET FhSession; // 會話句柄
HINTERNET FhConnect; // http連接句柄
HINTERNET FhRequest; // http請求句柄
bool FSuccess; // 下載是否成功
int iFileHandle; // 輸出文件的句柄