• <ins id="pjuwb"></ins>
    <blockquote id="pjuwb"><pre id="pjuwb"></pre></blockquote>
    <noscript id="pjuwb"></noscript>
          <sup id="pjuwb"><pre id="pjuwb"></pre></sup>
            <dd id="pjuwb"></dd>
            <abbr id="pjuwb"></abbr>

            C++樂園

            C/C++ 交流

              C++博客 :: 首頁 :: 新隨筆 :: 聯(lián)系 :: 聚合  :: 管理 ::
              12 隨筆 :: 18 文章 :: 14 評論 :: 0 Trackbacks
             ftp下載的好處我在這里就不多說了,許多工程會把ftp下載作為一個重要的功能來實現(xiàn)。微軟提供的WinInet類可以利用下面這些函數(shù):

              InternetOpen;
              InternetConnect;
              GetCurrentDirectory;
              SetCurrentDirectory;
              FtpGetFile;

              很容易實現(xiàn)ftp的下載,網(wǎng)上關(guān)于這方面的文章也很多。但是要實現(xiàn)ftp的多線程下載,利用這些函數(shù)就顯得有些牽強了。用socket根據(jù)ftp協(xié)議來開發(fā)將會變的十分靈活。下面我就逐步的講解整個開發(fā)的過程:開發(fā)環(huán)境 BCB(組件模式),VC 環(huán)境下請自行稍作改動。看了這篇文章后對于BCB開發(fā)人員來說,不僅可以對 FlashGet 等軟件的開發(fā)原理有一定的了解,特別是在開發(fā)組件方面也有很大的指導(dǎo)作用,請耐心的將它看完。很簡單!!

              首先介紹一下部分ftp協(xié)議:

            圖一 FTP服務(wù)示意圖

              用戶FTP和服務(wù)器FTP之間要傳送文件,需要有兩個連接:命令通道和數(shù)據(jù)連接,從名字上就可以看出命令通道是傳送命令的,數(shù)據(jù)通道是用于傳送文件。服務(wù)器與服務(wù)器之間的數(shù)據(jù)傳送在此就不多作解釋。
            主要用到的命令為:USER,PASS,TYPE,SIZE,REST,CWD,PWD,RETR,PASV,PORT,QUIT;

              USER:參數(shù)是標記用戶的Telnet串。用戶標記是訪問服務(wù)器必須的,此命令通常是控制連接后第一個發(fā)出的命令,有些主機還會要求口令和帳戶。服務(wù)器可以在任何時間接收新的USER命令以改變訪問控制和(或)帳戶信息。這可以重新開始登錄過程,所以傳輸參數(shù)不變,在進行中的文件傳輸在過去的訪問控制參數(shù)下完成。
            PASS:參數(shù)是標記用戶口令的Telnet串。此命令緊跟USER命令,在某些站點它是完成訪問控制不可缺少的一步。因此口令是個重要的東西,因此不能顯示出來,服務(wù)器方?jīng)]有辦法隱藏口令,所以這一任務(wù)得由用戶FTP進程完成。

              TYPE:參數(shù)指定表示類型。有些類型需要第二個參數(shù),第一個參數(shù)由單個Telnet字符定義,第二個參數(shù)是十進制整數(shù)指定字節(jié)大小,參數(shù)間以<SP>分隔。下面是格式:

            圖二 TYPE參數(shù)示意圖

              默認表示類型是ASCII非打印字符,如果參數(shù)未改變,以后只改變了第一個參數(shù),則使用默認值。

              SIZE:參數(shù)從FTP服務(wù)器上返回指定文件的大小。

              REST:參數(shù)域代表服務(wù)器要重新開始的那一點,此命令并不傳送文件,而是略過指定點后的數(shù)據(jù),此命令后應(yīng)該跟其它要求文件傳輸?shù)腇TP命令。

              CWD:此命令使用戶可以在不同的目錄或數(shù)據(jù)集下工作而不用改變它的登錄或帳戶信息。傳輸參數(shù)也不變。參數(shù)一般是目錄名或與系統(tǒng)相關(guān)的文件集合。

              PWD:改變當(dāng)前的工作目錄。

              RETR:開始傳送指定的文件。(從REST參數(shù)指定的偏移量開始傳送)

              PASV:此命令要求服務(wù)器DTP在指定的數(shù)據(jù)端口偵聽,進入被動接收請求的狀態(tài),參數(shù)是主機和端口地址。

              PORT:參數(shù)是要使用的數(shù)據(jù)連接端口,通常情況下對此不需要命令響應(yīng)。如果使用此命令時,要發(fā)送32位的IP地址和16位的TCP端口號。上面的信息以8位為一組,逗號間隔十進制傳輸。

              QUIT:退出登錄。
            各個參數(shù)的具體用法舉例如下:

            USER sandy \r\n //用戶名為sandy登錄
            PASS sandy \r\n //密碼為sandy
            TYPE I \r\n
            SIZE sandy.txt \r\n //如果sandy.txt文件存在,則返回該文件的大小
            REST 100 \r\n //重新指定文件傳送的偏移
            CWD infor/ \r\n //獲取當(dāng)前的工作目錄
            PWD temp/ \r\n //改變當(dāng)前的工作目錄
            RETR \r\n //開始傳送文件
            PASV \r\n //進入被動模式
            PORT h1,h2,h3,h4,p1,p2 \r\n //進入主動模式,h1,h2,h3,h4為ip地址的4個部分。p1,p2是16進制的端口號


              下面介紹一下各個函數(shù)的使用順序和一些應(yīng)注意的地方:

              使用這些命令的前提條件是客戶端和服務(wù)器端建立了連接。比如ftp服務(wù)器地址:192.168.1.81 ,端口:21。那么利用Winsock的API函數(shù)建立socket連接,然后使用USER,PASS登陸FTP服務(wù)器.需要下載文件,要確保文件必須在當(dāng)前工作目錄下,可以使用命令CWD和PWD。查看和更改當(dāng)前的工作目錄。使用SIZE命令獲取文件的大小。我們想要多線程下載那么就要求服務(wù)器支持該功能。一般我們都會在開頭先使用REST命令判斷該ftp站點是否支持多線程下載。PORT和PASV兩個命令是用來建立數(shù)據(jù)連接的。他們的主要區(qū)別是:PORT需要你指定一個ip地址和端口與服務(wù)器建立連接。PASV命令服務(wù)器會返回h1,h2,h3,h4,p1,p2樣式 的數(shù)據(jù)供客戶端連接。等數(shù)據(jù)連接建立后,就可以了使用REST,RETR進行多線程和斷點續(xù)傳文件下載了。

              上面講解了一點ftp下載的基本知識,下面主要介紹的是斷點續(xù)傳的文件保存技巧。

              若要講斷點續(xù)傳的文件保存方式至少可以說出10種,但是各種方法都有利有弊,下面主要介紹一種我在工作中常常使用的一種文件保存方式:比如要下載一個364544字節(jié)的文件,文件名為:namelock.avi。因為要斷點續(xù)傳,所以 在下載的過程中必須得保存文件的大小,已經(jīng)下載的文件的大小和各個線程的任務(wù)。

              有兩種方法:

              一、可以產(chǎn)生兩個文件:內(nèi)容文件和配置文件。

              二、只需一個文件:把配置文件的數(shù)據(jù)加載到內(nèi)容文件的末尾。

              這兩個都不失為好方法。我使用的是前一種,因為我水平有限,(對于臨界資源的訪問總是不能做到互坼,老出問題。)。這里 的后綴名希望大家要把它放在心上,后綴名是個象征性的東西。就拿我們公司來說,擁有自己的MPEG編碼、解碼技術(shù),比如原來5m的一首mp3歌曲,通過編碼可以 轉(zhuǎn)換成500K左右的.fun文件(funinhand的前三個字)。再利用我們自己的解碼播放器邊下載邊解碼邊播放, 音質(zhì)和mp3不相上下。真正實現(xiàn)了手機上的流媒體技術(shù)。受到國內(nèi)外高科技大公司的信賴。(不好意思,這里有點像做廣告了。)講這些的另外一個企圖是這樣的:

              內(nèi)容文件所使用的后綴名是我女朋友的英文名(namelock)的前三個字母.nam 。配置文件使用的是我自己的英文名(sandy)的前三個字母.san 。所以說寫程序也可以很浪漫,因為這,女朋友又給了我的月生活零用錢增加了幾元,哈哈(大家也可以效仿)。言歸正傳,這兩個文件嚴格意義上來講是臨時文件,當(dāng)文件下載完畢的時候,namelock.avi.nam內(nèi)容文件應(yīng)該改名為:namelock.avi。namelock.avi.san配置文件也應(yīng)該及時的刪除。
            FTP多線程下載技術(shù)部分:前面我介紹了文件的保存技巧,主要也是為了多線程服務(wù)。現(xiàn)在有個namelock.avi文件需要下載。文件的大小為:364544字節(jié)。要用8個下載線程。 第一步:將namelock.avi文件分成8個子模塊。這里要注意的地方是我所說的分成8個字模塊,并不是把文件的內(nèi)容分別存放到8個不同的緩沖區(qū)里。而是生成8個不同的文件偏移量。很多時候程序員為了偷懶往往容易一次性講文件讀入內(nèi)存,這樣帶來的后果是不堪設(shè)想的。一個比較理想的方法是這樣的。

            bool DealFile(string fileName) //隨便寫個函數(shù)說明
            {
            FILE *file;
            DWORD fileSize ,pos;
            int readLen ;

            //MAX_BUFFER_LEN 在頭文件里定義,這里能夠保證數(shù)據(jù)不丟失,也不至于內(nèi)存逸出
            char *buffer = new char[MAX_BUFFER_LEN];
            file = fopen(fileName.c_str(),"r+b");
              if(file == NULL) return false;
            fseek(file,0,2);
            fileSize = ftell(file); //取得文件的大小
            fseek(file,0,0);
            do{
            readLen = fread(buffer,sizeof(char),MAX_BUFFER_LEN,file);
            if(readLen > 0)
            {
            pos += readLen;
            //對讀取的文件做處理
            }
            }while(pos < fileSize); //循環(huán)讀取文件
             delete[] buffer;
            fclose(file); //釋放資源
            return true;

            }


              8個線程下載文件時,都要對內(nèi)容文件和配置文件進行讀寫。這樣如果沒有處理好,很有可能會造成訪問文件失敗,我定義了一個全局變量FileLocked,如果FileLocked=true說明文件正在被某個線程訪問。所以使用Sleep(10)睡眠等待。當(dāng)某個線程進入讀寫文件時必須設(shè)置FileLocked = true;訪問文件完畢必須將FileLocked = false;這樣就能很好的控制各個線程對文件的訪問了。(對臨界資源的訪問有API提供了很多很好的解決方法,請查閱)。

              8個下載線程同時下載文件時,完成部分下載是隨機的。那么怎么樣把隨機的文件數(shù)據(jù)按照偏移量正確的寫入文件呢?我是這樣實現(xiàn)的,當(dāng)要下載文件namelock.avi時,首先查找文件namelock.avi.san配置文件是否存在。如果存在,說明上次已經(jīng)下載過部分該文件,就可以斷點續(xù)傳了。如果沒有找到該文件,那么生成和該文件的大小一樣大的文件,文件里所有的數(shù)據(jù)都為0,(可以使用函數(shù)memset(buffer,10000,''0''))和一個配置文件。然后利用fseek函數(shù)將數(shù)據(jù)正確的覆蓋原先的0;接下來要介紹一寫配置文件的格式了。

              很簡單,配置文件的內(nèi)容主要包括:文件在本地保存的絕對路徑、文件的大小、線程的個數(shù)、已經(jīng)下載的文件大小,各個線程的任務(wù)(在原始文件起始位置和結(jié)束位置,中間使用''-''分開);如:

            D:\mm\namelock.avi //文件保存在這里
            364544 //文件大小
            5 //有5個線程在下載
            0 //已經(jīng)下載了0字節(jié)
            0-72908 //線程1的下載任務(wù)
            72908-145816 //線程2的下載任務(wù)
            145816-218724 //線程3的下載任務(wù)
            218724-291632 //線程4的下載任務(wù)
            291632-364544 //線程5的下載任務(wù)


              以上是開始下載時的各個線程的任務(wù)分配。

            D:\mm\namelock.avi
            364544
            5
            113868
            72908-72908
            113868-145816
            145816-218724
            218724-291632
            291632-364544


              以上是某一時刻各個線程的任務(wù)分配情況。

              各個線程任務(wù)分配是這樣實現(xiàn)的。在開始下載時,文件平均分成若干塊進行下載。如第一個線程一開始的任務(wù)是從文件的0位置開始下載一直到72908位置處。線程1每次下載一塊數(shù)據(jù)后就要調(diào)整任務(wù),如第一次下載了20800字節(jié)的數(shù)據(jù),那么線程1的任務(wù)將改為:20800-72908。如此下去,直到任務(wù)為72908-72908時表示線程1完成了當(dāng)前的下載任務(wù)。此時,線程1就分析各個線程的任務(wù),找出任務(wù)最為繁忙的一個線程:如線程3:14816-218724。那么線程1就自動去調(diào)整任務(wù),拿50%的任務(wù)來再次下載。周而復(fù)始直到各個線程都完成任務(wù)。不過這里有一點需要注意:為了避免重復(fù)下載部分數(shù)據(jù),在調(diào)整任務(wù)的時候,起始的文件便移量必須加上接受緩沖器的字節(jié)數(shù),因為如前面所舉的列子來看。線程1和線程3在平衡負載的時候,線程正在下載數(shù)據(jù),如果所剩的數(shù)據(jù)比接受緩沖器的大小還小,線程1和線程3的部分下載數(shù)據(jù)將會重復(fù)。

              在調(diào)整任務(wù)和分析任務(wù)的時候,會發(fā)現(xiàn)一個問題。就是讀取文件數(shù)據(jù)太過頻繁。于是我用了一個數(shù)據(jù)結(jié)構(gòu)。在下載文件的過程中始終打開配置文件,這樣速度提高了很多。在文件下載完畢后關(guān)閉文件。數(shù)據(jù)結(jié)構(gòu)如下:

            typedef struct FromToImpl{
            DWORD from; //任務(wù)起始位置
            DWORD to; //任務(wù)結(jié)束位置
            }m_fromTo;
            typedef struct InfroImpl{
            String fileLoad; //文件保存位置
            DWORD fileSize; //文件大小
            int threadCnt; //下載線程數(shù)
            DWORD alreadyDownloadCnt; //已經(jīng)下載的文件大小
            FromToImpl *fromToImpl; //各個線程的任務(wù)描述
            }m_inforImpl;

            posted on 2006-12-09 18:19 小不懂^_^ 閱讀(546) 評論(0)  編輯 收藏 引用

            只有注冊用戶登錄后才能發(fā)表評論。
            網(wǎng)站導(dǎo)航: 博客園   IT新聞   BlogJava   博問   Chat2DB   管理


            国产香蕉97碰碰久久人人| 久久精品国产精品亚洲艾草网美妙| 久久久久久久国产免费看| 四虎影视久久久免费| 狠狠色婷婷久久一区二区三区| 丰满少妇高潮惨叫久久久| 久久青青国产| 久久精品国产亚洲AV嫖农村妇女| 国产高潮久久免费观看| 中文字幕久久波多野结衣av| 夜夜亚洲天天久久| 伊人久久大香线蕉综合影院首页| 青青热久久综合网伊人| 天天躁日日躁狠狠久久| 久久中文字幕视频、最近更新 | 久久久久综合网久久| 日韩精品久久久久久久电影| 91精品国产高清久久久久久91| 久久99热这里只频精品6| 一本色道久久88加勒比—综合| 国内高清久久久久久| 一本大道久久东京热无码AV| 国产成人精品久久亚洲高清不卡 | 狠狠色狠狠色综合久久| 伊人久久大香线蕉精品不卡| 品成人欧美大片久久国产欧美| 7777久久亚洲中文字幕| 亚洲精品午夜国产VA久久成人| 香蕉久久永久视频| 中文字幕无码久久精品青草| 性做久久久久久久久老女人 | 亚洲精品无码久久久| 91精品国产91久久| 99久久精品九九亚洲精品| 97久久精品无码一区二区| 久久精品亚洲中文字幕无码麻豆| 偷窥少妇久久久久久久久| 色播久久人人爽人人爽人人片AV| 成人综合久久精品色婷婷| 亚洲综合日韩久久成人AV| 亚洲va中文字幕无码久久不卡|