周學威,閆鑫,趙櫸云,楊薇
(中北大學 儀器科學與動態測試教育部重點實驗室,山西 太原 030051)
多線程就是允許單個程序創建多個并行執行的線程來完成各自的任務,它在多任務和實時處理等方面具有重要意義,特別是在網絡應用程序中,可以提高帶寬利用率和程序反應速度[1]。為了達到下載大型網站的目的,在編寫離線瀏覽軟件的過程中,需要下載大量的Web文件,其中一個技術要點就是多線程下載問題。是否具有“多線程下載”技術、甚至能支持多少個下載線程都成了人們評測下載軟件的要素[2]。本文以SOCKET技術為依托,基于VC++6.0環境,采用HTTP協議,通過編寫客戶端應用程序,對文件的多線程下載進行了設計與實現。
多線程下載的前提是網絡的連通和通信軟件與協議的應用。下載文件的過程是客戶端與服務器的交互的過程,在下載過程中采用的傳送文件的協議有多種,本程序的設計采用了HTTP協議[3]。
HTTP即超文本傳輸協議,當客戶端與服務器建立一個TCP連接后,客戶端就可以發送請求并讀取服務器的消息響應。在網絡通信環境下,Socket作為應用程序和網絡之間的標準接口,可以看成在兩個程序進行通訊連接中的一個端點,是連接應用程序和網絡驅動程序的橋梁[4]。
MFC是VC編程環境最重要的組成部分,它為用戶提供了一大批預先定義的類和成員函數,封裝了大量的Windows API[5]。為了簡化套接字網絡編程,更方便的利用Windows系統的消息驅動機制,充分利用MFC的優勢,本設計采用基于MFC對話框的架構。
由于本設計基于HTTP協議,采用MFC WinSock中的CSocket套接字類進行編程,由客戶端直接發送請求到服務器端進行資源的下載,因此只需編寫客戶端網絡應用程序[6]。首先,根據HTTP協議,構造請求消息頭,向Web服務器發送資源下載請求,當服務器返回請求成功后,再分別為每個線程構造下載請求,通過CSocket編程向服務器傳輸請求,實現各個線程的下載;然后,啟動線程函數,包括四個下載線程用以實現多線程下載和一個監聽線程來實時記錄下載狀態;最后當各個下載線程都結束時,進行文件合并,同時刪除臨時文件以完成下載任務[7]。
實現過程可概括為:利用Socket套接字發送消息,在發送的消息中要構造請求消息字段,用HTTP協議向服務器發送下載請求,通過服務器的返回指令,實現資源下載,并通過啟動線程函數來加入多線程技術,從而實現數據的多線程下載。其函數調用過程如圖1所示。
要實現下載,必須由客戶端向服務器發送請求消息,這是HTTP的核心。如圖1所示,開始下載后,首先要獲得要下載文件的URL,接著調用ParseURL函數判斷要下載文件的URL是否合理,若合理,則會由SendRequest()函數向主線程發送HTTP請求消息,在調用GetInfo函數獲取HTTP服務器成功響應的消息后,會為每個下載線程分配要下載的字節數。這樣,開始下載任務成功實現后,會調用CDownloadDlg類中的CreateThread()函數中,創建線程,DownloadThread (LPVOID lpParam)是新建線程的入口函數,ThreadFunc(index)函數主要進行下載過程中每個子線程的套接字編程,通過編寫向服務器發送請求消息的標題字段的代碼,利用HTTP協議的下載原理實現每個子線程的下載。

圖1 多線程下載的函數調用過程
(1)創建下載線程,指向DownloadThread()這個線程函數的入口:
m_hThread[i]=::CreateThread(NULL, 0, DownloadThread,(LPVOID)&http, 0, &dwThread);
(2)創建監聽線程,指向監聽線程Notify()函數的入口,用以對各線程的下載狀態和進度進行監聽:
m_hNotify=::CreateThread(NULL,0,Notify,(LPVOID)this,0,&dwNotify);
(3)DownloadThread()函數:
DWORD WINAPI DownloadThread
(LPVOID lpParam);該函數會通過公有的繼承方式繼承ThreadFunc(index)函數,ThreadFunc(index)函數實現的主要功能是:每個子線程向服務器發送HTTP請求消息,實現每個子線程下載任務的完成。
(4)Notify()函數
DWORD WINAPI Notify(LPVOID lpParam);該函數中,會通過公有的繼承方式繼承在類CDownloadDlg中的Finish()函數,判斷每個子線程是否完成下載,并進行下載文件的保存。
(5)ThreadFunc()函數
在ThreadFunc函數中,先要創建客戶端的套接字對象,對每個要下載的子線程,設置了HTTP會話中的請求消息字段,通過由客戶端向服務器發送請求消息實現每個子線程的下載。對于多線程下載,在請求消息的標題字段增加了Range,用于請求服務器返回指定大小的字段,其大小采用了數據分片技術來確定[8]。編寫的語句為strRange.Format("Range: bytes=%d-%d ", m_state.range[2 * index], m_state.range[2 * index + 1]),其中index是線程的序列號。
程序設計完成后,編譯并運行,在生成對話框后,選擇要下載文件的URL,將其直接拖入GetList列表控件中,點擊開始按鈕,開始下載。下載過程如圖2所示,可見4個線程同步運行,實現了4個線程下載同一文件的多線程下載;下載完成時,彈出提示窗口提示“多線程下載完成”,如圖3所示。此時單擊提示窗口中的“確定”按鈕,4個.jpg臨時文件將合并成一個JPEG圖像,至此便完成了多線程文件的下載。

圖2 正在下載界面

圖3 下載完成界面
本文對多線程數據傳輸進行了詳細分析和總體設計,對多線程下載的原理、具體實現和應用進行了探討,采用了面向對象的設計方法,應用多線程技術,通過Windows套接字函數,直接向Web服務器發送請求,用VC++6.0 MFC中的CSocket類編寫客戶端程序,實現了基于超文本傳輸協議HTTP的文件下載,通過創建和編寫線程函數實現了多線程傳輸,經測試,下載速度有一定的改善。
[1]鄭阿奇.Visual C++實用教程[M].3版.北京:電子工業出版社,2008.
[2]毛光喜.多線程下載工具的開發與應用[J].計算機應用與軟件,2006,23(7):136-138.
[3]Charles Wright.VisualC++程序員使用大全[M].鄧勁生,張曉明 譯.北京:中國水利水電出版社,2001.
[4]蔣東興.WindowsSockets網絡程序設計大全[M].北京:清華大學出版社,1999.
[5]李晶媛.基于HTTP協議的多線程下載工具的實現[J].電腦開發與應用,2009,22(10):52-54.
[6]孫鑫,余安萍.VC++深入詳解[M].北京:電子工業出版社,2007.
[7]趙輝,葉子青.VisualC++系統開發實例精粹[M].3版.北京:人民郵電出版社,2006.
[8]孫輝霞.基于VC++的多線程編程實現[J].中國電子商務,2010(3):61-62.