周學威,閆鑫,趙櫸云,楊薇
(中北大學 儀器科學與動態(tài)測試教育部重點實驗室,山西 太原 030051)
多線程就是允許單個程序創(chuàng)建多個并行執(zhí)行的線程來完成各自的任務(wù),它在多任務(wù)和實時處理等方面具有重要意義,特別是在網(wǎng)絡(luò)應(yīng)用程序中,可以提高帶寬利用率和程序反應(yīng)速度[1]。為了達到下載大型網(wǎng)站的目的,在編寫離線瀏覽軟件的過程中,需要下載大量的Web文件,其中一個技術(shù)要點就是多線程下載問題。是否具有“多線程下載”技術(shù)、甚至能支持多少個下載線程都成了人們評測下載軟件的要素[2]。本文以SOCKET技術(shù)為依托,基于VC++6.0環(huán)境,采用HTTP協(xié)議,通過編寫客戶端應(yīng)用程序,對文件的多線程下載進行了設(shè)計與實現(xiàn)。
多線程下載的前提是網(wǎng)絡(luò)的連通和通信軟件與協(xié)議的應(yīng)用。下載文件的過程是客戶端與服務(wù)器的交互的過程,在下載過程中采用的傳送文件的協(xié)議有多種,本程序的設(shè)計采用了HTTP協(xié)議[3]。
HTTP即超文本傳輸協(xié)議,當客戶端與服務(wù)器建立一個TCP連接后,客戶端就可以發(fā)送請求并讀取服務(wù)器的消息響應(yīng)。在網(wǎng)絡(luò)通信環(huán)境下,Socket作為應(yīng)用程序和網(wǎng)絡(luò)之間的標準接口,可以看成在兩個程序進行通訊連接中的一個端點,是連接應(yīng)用程序和網(wǎng)絡(luò)驅(qū)動程序的橋梁[4]。
MFC是VC編程環(huán)境最重要的組成部分,它為用戶提供了一大批預先定義的類和成員函數(shù),封裝了大量的Windows API[5]。為了簡化套接字網(wǎng)絡(luò)編程,更方便的利用Windows系統(tǒng)的消息驅(qū)動機制,充分利用MFC的優(yōu)勢,本設(shè)計采用基于MFC對話框的架構(gòu)。
由于本設(shè)計基于HTTP協(xié)議,采用MFC WinSock中的CSocket套接字類進行編程,由客戶端直接發(fā)送請求到服務(wù)器端進行資源的下載,因此只需編寫客戶端網(wǎng)絡(luò)應(yīng)用程序[6]。首先,根據(jù)HTTP協(xié)議,構(gòu)造請求消息頭,向Web服務(wù)器發(fā)送資源下載請求,當服務(wù)器返回請求成功后,再分別為每個線程構(gòu)造下載請求,通過CSocket編程向服務(wù)器傳輸請求,實現(xiàn)各個線程的下載;然后,啟動線程函數(shù),包括四個下載線程用以實現(xiàn)多線程下載和一個監(jiān)聽線程來實時記錄下載狀態(tài);最后當各個下載線程都結(jié)束時,進行文件合并,同時刪除臨時文件以完成下載任務(wù)[7]。
實現(xiàn)過程可概括為:利用Socket套接字發(fā)送消息,在發(fā)送的消息中要構(gòu)造請求消息字段,用HTTP協(xié)議向服務(wù)器發(fā)送下載請求,通過服務(wù)器的返回指令,實現(xiàn)資源下載,并通過啟動線程函數(shù)來加入多線程技術(shù),從而實現(xiàn)數(shù)據(jù)的多線程下載。其函數(shù)調(diào)用過程如圖1所示。
要實現(xiàn)下載,必須由客戶端向服務(wù)器發(fā)送請求消息,這是HTTP的核心。如圖1所示,開始下載后,首先要獲得要下載文件的URL,接著調(diào)用ParseURL函數(shù)判斷要下載文件的URL是否合理,若合理,則會由SendRequest()函數(shù)向主線程發(fā)送HTTP請求消息,在調(diào)用GetInfo函數(shù)獲取HTTP服務(wù)器成功響應(yīng)的消息后,會為每個下載線程分配要下載的字節(jié)數(shù)。這樣,開始下載任務(wù)成功實現(xiàn)后,會調(diào)用CDownloadDlg類中的CreateThread()函數(shù)中,創(chuàng)建線程,DownloadThread (LPVOID lpParam)是新建線程的入口函數(shù),ThreadFunc(index)函數(shù)主要進行下載過程中每個子線程的套接字編程,通過編寫向服務(wù)器發(fā)送請求消息的標題字段的代碼,利用HTTP協(xié)議的下載原理實現(xiàn)每個子線程的下載。
圖1 多線程下載的函數(shù)調(diào)用過程
(1)創(chuàng)建下載線程,指向DownloadThread()這個線程函數(shù)的入口:
m_hThread[i]=::CreateThread(NULL, 0, DownloadThread,(LPVOID)&http, 0, &dwThread);
(2)創(chuàng)建監(jiān)聽線程,指向監(jiān)聽線程Notify()函數(shù)的入口,用以對各線程的下載狀態(tài)和進度進行監(jiān)聽:
m_hNotify=::CreateThread(NULL,0,Notify,(LPVOID)this,0,&dwNotify);
(3)DownloadThread()函數(shù):
DWORD WINAPI DownloadThread
(LPVOID lpParam);該函數(shù)會通過公有的繼承方式繼承ThreadFunc(index)函數(shù),ThreadFunc(index)函數(shù)實現(xiàn)的主要功能是:每個子線程向服務(wù)器發(fā)送HTTP請求消息,實現(xiàn)每個子線程下載任務(wù)的完成。
(4)Notify()函數(shù)
DWORD WINAPI Notify(LPVOID lpParam);該函數(shù)中,會通過公有的繼承方式繼承在類CDownloadDlg中的Finish()函數(shù),判斷每個子線程是否完成下載,并進行下載文件的保存。
(5)ThreadFunc()函數(shù)
在ThreadFunc函數(shù)中,先要創(chuàng)建客戶端的套接字對象,對每個要下載的子線程,設(shè)置了HTTP會話中的請求消息字段,通過由客戶端向服務(wù)器發(fā)送請求消息實現(xiàn)每個子線程的下載。對于多線程下載,在請求消息的標題字段增加了Range,用于請求服務(wù)器返回指定大小的字段,其大小采用了數(shù)據(jù)分片技術(shù)來確定[8]。編寫的語句為strRange.Format("Range: bytes=%d-%d ", m_state.range[2 * index], m_state.range[2 * index + 1]),其中index是線程的序列號。
程序設(shè)計完成后,編譯并運行,在生成對話框后,選擇要下載文件的URL,將其直接拖入GetList列表控件中,點擊開始按鈕,開始下載。下載過程如圖2所示,可見4個線程同步運行,實現(xiàn)了4個線程下載同一文件的多線程下載;下載完成時,彈出提示窗口提示“多線程下載完成”,如圖3所示。此時單擊提示窗口中的“確定”按鈕,4個.jpg臨時文件將合并成一個JPEG圖像,至此便完成了多線程文件的下載。
圖2 正在下載界面
圖3 下載完成界面
本文對多線程數(shù)據(jù)傳輸進行了詳細分析和總體設(shè)計,對多線程下載的原理、具體實現(xiàn)和應(yīng)用進行了探討,采用了面向?qū)ο蟮脑O(shè)計方法,應(yīng)用多線程技術(shù),通過Windows套接字函數(shù),直接向Web服務(wù)器發(fā)送請求,用VC++6.0 MFC中的CSocket類編寫客戶端程序,實現(xiàn)了基于超文本傳輸協(xié)議HTTP的文件下載,通過創(chuàng)建和編寫線程函數(shù)實現(xiàn)了多線程傳輸,經(jīng)測試,下載速度有一定的改善。
[1]鄭阿奇.Visual C++實用教程[M].3版.北京:電子工業(yè)出版社,2008.
[2]毛光喜.多線程下載工具的開發(fā)與應(yīng)用[J].計算機應(yīng)用與軟件,2006,23(7):136-138.
[3]Charles Wright.VisualC++程序員使用大全[M].鄧勁生,張曉明 譯.北京:中國水利水電出版社,2001.
[4]蔣東興.WindowsSockets網(wǎng)絡(luò)程序設(shè)計大全[M].北京:清華大學出版社,1999.
[5]李晶媛.基于HTTP協(xié)議的多線程下載工具的實現(xiàn)[J].電腦開發(fā)與應(yīng)用,2009,22(10):52-54.
[6]孫鑫,余安萍.VC++深入詳解[M].北京:電子工業(yè)出版社,2007.
[7]趙輝,葉子青.VisualC++系統(tǒng)開發(fā)實例精粹[M].3版.北京:人民郵電出版社,2006.
[8]孫輝霞.基于VC++的多線程編程實現(xiàn)[J].中國電子商務(wù),2010(3):61-62.